EEVEE: Anisotropic filtering

This PR adds a render engine specific quality setting for anisotropic
filtering. The User preference option is still used in other places
(Clip editor, sampling, ...).

The implementation generates all sampler permutation and select the one
that is needed on a per case base. When building the GPU material from
the shader graph anisotropic filtering can be requested by setting the
`GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE` flag. When generating the
draw pass for the material, the sampler is being updated to match
`scene.render.anisotropic_filter`.

Pull Request: https://projects.blender.org/blender/blender/pulls/155111
This commit is contained in:
Jeroen Bakker 2026-03-09 10:27:38 +01:00
parent b72f02a979
commit 69aa93bb55
27 changed files with 192 additions and 124 deletions

View file

@ -808,6 +808,7 @@ class RENDER_PT_eevee_performance(RenderButtonsPanel, Panel):
layout.use_property_decorate = False # No animation.
layout.prop(rd, "use_high_quality_normals")
layout.prop(rd, "anisotropic_filter")
class CompositorPerformanceButtonsPanel:

View file

@ -30,7 +30,7 @@ namespace blender {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 6
#define BLENDER_FILE_SUBVERSION 7
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View file

@ -105,6 +105,12 @@ void blo_do_versions_520(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 7)) {
for (Scene &scene : bmain->scenes) {
scene.r.anisotropic_filter = 2;
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View file

@ -192,6 +192,8 @@ void Instance::init(const int2 &output_res,
is_image_render = true;
}
anisotropic_filtering = GPU_anisotropic_filtering_flags(scene->r.anisotropic_filter);
sampling.init(scene);
camera.init();
film.init(output_res, output_rect);

View file

@ -174,6 +174,8 @@ class Instance : public DrawEngine {
bool use_curves = true;
bool use_volumes = true;
GPUSamplerFiltering anisotropic_filtering = GPU_SAMPLER_FILTERING_DEFAULT;
/** Debug mode from debug value. */
eDebugMode debug_mode = eDebugMode::DEBUG_NONE;

View file

@ -410,7 +410,7 @@ void LookdevModule::sync_pass(PassSimple &pass,
GPUMaterial *gpumat = inst_.shaders.material_shader_get(
mat, mat->nodetree, MAT_PIPE_FORWARD, MAT_GEOM_MESH, false, inst_.materials.default_surface);
pass.state_set(state);
pass.material_set(*inst_.manager, gpumat);
pass.material_set(*inst_.manager, gpumat, false, inst_.anisotropic_filtering);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
pass.bind_resources(inst_.uniform_data);
pass.bind_resources(inst_.lights);

View file

@ -282,7 +282,8 @@ MaterialPass MaterialModule::material_pass_get(Object *ob,
if (shader_sub != nullptr) {
/* Create a sub for this material as `shader_sub` is for sharing shader between materials. */
matpass.sub_pass = &shader_sub->sub(GPU_material_get_name(matpass.gpumat));
matpass.sub_pass->material_set(*inst_.manager, matpass.gpumat, true);
matpass.sub_pass->material_set(
*inst_.manager, matpass.gpumat, true, inst_.anisotropic_filtering);
}
else {
matpass.sub_pass = nullptr;

View file

@ -53,7 +53,7 @@ void BackgroundPipeline::sync(GPUMaterial *gpumat,
world_ps_.init();
world_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_CLIP_CONTROL_UNIT_RANGE |
DRW_STATE_DEPTH_EQUAL);
world_ps_.material_set(manager, gpumat);
world_ps_.material_set(manager, gpumat, false, inst_.anisotropic_filtering);
world_ps_.push_constant("world_opacity_fade", background_opacity);
world_ps_.push_constant("world_background_blur", square_f(background_blur));
SphereProbeData &world_data = *static_cast<SphereProbeData *>(&inst_.light_probes.world_sphere_);
@ -106,7 +106,7 @@ void WorldPipeline::sync(GPUMaterial *gpumat)
pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS);
Manager &manager = *inst_.manager;
pass.material_set(manager, gpumat);
pass.material_set(manager, gpumat, false, inst_.anisotropic_filtering);
pass.push_constant("world_opacity_fade", 1.0f);
pass.push_constant("world_background_blur", 0.0f);
pass.push_constant("world_coord_packed", int4(0.0f));
@ -162,7 +162,7 @@ void WorldVolumePipeline::sync(GPUMaterial *gpumat)
world_ps_.bind_resources(inst_.volume.properties);
world_ps_.bind_resources(inst_.sampling);
world_ps_.material_set(*inst_.manager, gpumat);
world_ps_.material_set(*inst_.manager, gpumat, false, inst_.anisotropic_filtering);
/* Bind correct dummy texture for attributes defaults. */
PassSimple::Sub *sub = volume_sub_pass(world_ps_, nullptr, nullptr, gpumat);
@ -467,7 +467,7 @@ PassMain::Sub *ForwardPipeline::prepass_transparent_add(const Object *ob,
float sorting_value = math::dot(float3(ob->object_to_world().location()), camera_forward_);
PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
pass->state_set(state);
pass->material_set(*inst_.manager, gpumat, true);
pass->material_set(*inst_.manager, gpumat, true, inst_.anisotropic_filtering);
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA) &&
GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT))
@ -498,7 +498,7 @@ PassMain::Sub *ForwardPipeline::material_transparent_add(const Object *ob,
float sorting_value = math::dot(float3(ob->object_to_world().location()), camera_forward_);
PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
pass->state_set(state);
pass->material_set(*inst_.manager, gpumat, true);
pass->material_set(*inst_.manager, gpumat, true, inst_.anisotropic_filtering);
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA) &&
GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT))
@ -1253,7 +1253,7 @@ PassMain::Sub *VolumeLayer::occupancy_add(const Object *ob,
is_empty = false;
PassMain::Sub *pass = &occupancy_ps_->sub(GPU_material_get_name(gpumat));
pass->material_set(*inst_.manager, gpumat, true);
pass->material_set(*inst_.manager, gpumat, true, inst_.anisotropic_filtering);
pass->push_constant("use_fast_method", use_fast_occupancy);
return pass;
}
@ -1267,7 +1267,7 @@ PassMain::Sub *VolumeLayer::material_add(const Object *ob,
UNUSED_VARS_NDEBUG(ob);
PassMain::Sub *pass = &material_ps_->sub(GPU_material_get_name(gpumat));
pass->material_set(*inst_.manager, gpumat, true);
pass->material_set(*inst_.manager, gpumat, true, inst_.anisotropic_filtering);
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_VOLUME_SCATTER)) {
has_scatter = true;
}

View file

@ -248,7 +248,8 @@ class PassBase {
*/
void material_set(Manager &manager,
GPUMaterial *material,
bool deferred_texture_loading = false);
bool deferred_texture_loading = false,
GPUSamplerFiltering anisotropic_filtering = GPU_SAMPLER_FILTERING_DEFAULT);
/**
* Record a draw call.
@ -1136,7 +1137,8 @@ inline void PassBase<T>::subpass_transition(GPUAttachmentState depth_attachment,
template<class T>
inline void PassBase<T>::material_set(Manager &manager,
GPUMaterial *material,
bool deferred_texture_loading)
bool deferred_texture_loading,
GPUSamplerFiltering anisotropic_filtering)
{
GPUPass *gpupass = GPU_material_get_pass(material);
shader_set(GPU_pass_shader_get(gpupass));
@ -1157,21 +1159,28 @@ inline void PassBase<T>::material_set(Manager &manager,
gputex = BKE_image_get_gpu_material_texture(tex->ima, iuser, use_tile_mapping);
}
GPUSamplerState sampler_state = tex->sampler_state;
/* If any anisotropic filtering is requested, reset it to the scene setting. */
if (sampler_state.filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE) {
sampler_state.disable_filtering_flag(GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK);
sampler_state.enable_filtering_flag(anisotropic_filtering);
}
if (*gputex.texture == nullptr) {
/* Texture not yet loaded. Register a reference inside the draw pass.
* The texture will be acquired once it is created. */
bind_texture(tex->sampler_name, gputex.texture, tex->sampler_state);
bind_texture(tex->sampler_name, gputex.texture, sampler_state);
if (gputex.tile_mapping) {
bind_texture(tex->tiled_mapping_name, gputex.tile_mapping, tex->sampler_state);
bind_texture(tex->tiled_mapping_name, gputex.tile_mapping, sampler_state);
}
}
else {
/* Texture is loaded. Acquire. */
manager.acquire_texture(*gputex.texture);
bind_texture(tex->sampler_name, *gputex.texture, tex->sampler_state);
bind_texture(tex->sampler_name, *gputex.texture, sampler_state);
if (gputex.tile_mapping) {
manager.acquire_texture(*gputex.tile_mapping);
bind_texture(tex->tiled_mapping_name, *gputex.tile_mapping, tex->sampler_state);
bind_texture(tex->tiled_mapping_name, *gputex.tile_mapping, sampler_state);
}
}
}

View file

@ -16,6 +16,7 @@
#include "BLI_assert.h"
#include "BLI_enum_flags.hh"
#include "BLI_index_range.hh"
#include "GPU_format.hh"
@ -306,10 +307,13 @@ enum GPUSamplerFiltering {
* Enable Anisotropic filtering. This only has effect if `GPU_SAMPLER_FILTERING_MIPMAP` is set.
* The filtered result is implementation dependent.
*
* The maximum amount of samples is always set to its maximum possible value and can't be
* changed, except by the user through the user preferences, see the use of U.anisotropic_filter.
* 3 bits are reserved to store the anisotropic option. When all bits are 0 it means that
* anisotropic filtering is off.
*/
GPU_SAMPLER_FILTERING_ANISOTROPIC = (1 << 2),
GPU_SAMPLER_FILTERING_ANISOTROPIC_2 = (2 << 2),
GPU_SAMPLER_FILTERING_ANISOTROPIC_4 = (3 << 2),
GPU_SAMPLER_FILTERING_ANISOTROPIC_8 = (4 << 2),
GPU_SAMPLER_FILTERING_ANISOTROPIC_16 = (5 << 2),
};
ENUM_OPERATORS(GPUSamplerFiltering)
@ -317,8 +321,58 @@ ENUM_OPERATORS(GPUSamplerFiltering)
/** The number of every possible filtering configuration. */
static const int GPU_SAMPLER_FILTERING_TYPES_COUNT = (GPU_SAMPLER_FILTERING_LINEAR |
GPU_SAMPLER_FILTERING_MIPMAP |
GPU_SAMPLER_FILTERING_ANISOTROPIC) +
GPU_SAMPLER_FILTERING_ANISOTROPIC_16) +
1;
/** Bit mask for selecting the GPU_SAMPLER_FILTERING_ANISOTROPIC_* bits. */
static constexpr GPUSamplerFiltering GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK =
(GPU_SAMPLER_FILTERING_ANISOTROPIC_2 | GPU_SAMPLER_FILTERING_ANISOTROPIC_4 |
GPU_SAMPLER_FILTERING_ANISOTROPIC_8 | GPU_SAMPLER_FILTERING_ANISOTROPIC_16);
/** Special flag where materials can request the desire to turn on/off anisotropic filtering. */
static constexpr GPUSamplerFiltering GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE =
(GPU_SAMPLER_FILTERING_ANISOTROPIC_2 | GPU_SAMPLER_FILTERING_ANISOTROPIC_4 |
GPU_SAMPLER_FILTERING_ANISOTROPIC_8 | GPU_SAMPLER_FILTERING_ANISOTROPIC_16);
/**
* Convenience function to create a GPUSamplerFiltering with only the anisotropic filtering flags
* set that represents the given samples.
*/
static inline GPUSamplerFiltering GPU_anisotropic_filtering_flags(int anisotropic_samples)
{
if (anisotropic_samples <= 1) {
return GPU_SAMPLER_FILTERING_DEFAULT;
}
if (IndexRange::from_begin_end_inclusive(2, 3).contains(anisotropic_samples)) {
return GPU_SAMPLER_FILTERING_ANISOTROPIC_2;
}
else if (IndexRange::from_begin_end_inclusive(4, 7).contains(anisotropic_samples)) {
return GPU_SAMPLER_FILTERING_ANISOTROPIC_4;
}
else if (IndexRange::from_begin_end_inclusive(8, 15).contains(anisotropic_samples)) {
return GPU_SAMPLER_FILTERING_ANISOTROPIC_8;
}
/* 16 or higher. */
return GPU_SAMPLER_FILTERING_ANISOTROPIC_16;
}
static inline int GPU_anisotropic_samples_get(GPUSamplerFiltering filtering_flags)
{
switch (filtering_flags & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK) {
case GPU_SAMPLER_FILTERING_ANISOTROPIC_2:
return 2;
case GPU_SAMPLER_FILTERING_ANISOTROPIC_4:
return 4;
case GPU_SAMPLER_FILTERING_ANISOTROPIC_8:
return 8;
case GPU_SAMPLER_FILTERING_ANISOTROPIC_16:
return 16;
default:
return 1;
}
BLI_assert_unreachable();
return 1;
}
/**
* The `GPUSamplerExtendMode` specifies how the texture will be extrapolated for out-of-bound
@ -569,8 +623,25 @@ struct GPUSamplerState {
serialized_parameters += "mipmap_";
}
if (this->filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC) {
serialized_parameters += "anisotropic_";
if ((this->filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK) ==
GPU_SAMPLER_FILTERING_ANISOTROPIC_2)
{
serialized_parameters += "anisotropic2x_";
}
else if ((this->filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK) ==
GPU_SAMPLER_FILTERING_ANISOTROPIC_4)
{
serialized_parameters += "anisotropic4x_";
}
else if ((this->filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK) ==
GPU_SAMPLER_FILTERING_ANISOTROPIC_8)
{
serialized_parameters += "anisotropic8x_";
}
else if ((this->filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK) ==
GPU_SAMPLER_FILTERING_ANISOTROPIC_16)
{
serialized_parameters += "anisotropic16x_";
}
switch (this->extend_x) {
@ -1025,7 +1096,7 @@ void GPU_texture_mipmap_mode(gpu::Texture *texture, bool use_mipmap, bool use_fi
/**
* Set anisotropic filter usage. Filter sample count is determined globally by
* `U.anisotropic_filter` and updated when `GPU_samplers_update` is called.
* `U.anisotropic_filter`.
*/
void GPU_texture_anisotropic_filter(gpu::Texture *texture, bool use_aniso);
@ -1217,11 +1288,6 @@ const char *GPU_texture_format_name(gpu::TextureFormat format);
*/
unsigned int GPU_texture_memory_usage_get();
/**
* Update sampler states depending on user settings.
*/
void GPU_samplers_update();
/** \} */
/* -------------------------------------------------------------------- */

View file

@ -34,7 +34,6 @@ class DummyBackend : public GPUBackend {
}
void init_resources() override {}
void delete_resources() override {}
void samplers_update() override {}
void compute_dispatch(int /*groups_x_len*/, int /*groups_y_len*/, int /*groups_z_len*/) override
{
}

View file

@ -55,7 +55,6 @@ class GPUBackend {
return compiler_;
}
virtual void samplers_update() = 0;
virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0;
virtual void compute_dispatch_indirect(StorageBuf *indirect_buf) = 0;

View file

@ -8,6 +8,8 @@
#include "BLI_string.h"
#include "DNA_userdef_types.h"
#include "GPU_framebuffer.hh"
#include "GPU_texture.hh"
@ -488,7 +490,8 @@ gpu::Texture *GPU_texture_create_view(const char *name,
if (GPU_texture_has_integer_format(view)) {
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, false);
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_MIPMAP, false);
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_ANISOTROPIC, false);
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK,
false);
}
return view;
@ -681,10 +684,13 @@ void GPU_texture_mipmap_mode(gpu::Texture *texture, bool use_mipmap, bool use_fi
void GPU_texture_anisotropic_filter(gpu::Texture *texture, bool use_aniso)
{
Texture *tex = texture;
int samples = use_aniso ? U.anisotropic_filter : 1;
/* Stencil and integer format does not support filtering. */
BLI_assert(!(use_aniso) ||
BLI_assert(!(samples > 1) ||
!(tex->format_flag_get() & (GPU_FORMAT_STENCIL | GPU_FORMAT_INTEGER)));
tex->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_ANISOTROPIC, use_aniso);
GPUSamplerFiltering filtering = GPU_anisotropic_filtering_flags(samples);
tex->sampler_state.disable_filtering_flag(GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK);
tex->sampler_state.enable_filtering_flag(filtering);
}
void GPU_texture_extend_mode_x(gpu::Texture *texture, GPUSamplerExtendMode extend_mode)
@ -1042,24 +1048,6 @@ GPUPixelBufferNativeHandle GPU_pixel_buffer_get_native_handle(GPUPixelBuffer *pi
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPU Sampler Objects
*
* Simple wrapper around opengl sampler objects.
* Override texture sampler state for one sampler unit only.
* \{ */
void GPU_samplers_update()
{
/* Backend may not exist when we are updating preferences from background mode. */
GPUBackend *backend = GPUBackend::get();
if (backend) {
backend->samplers_update();
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name GPU texture utilities
* \{ */

View file

@ -51,7 +51,6 @@ class MTLBackend : public GPUBackend {
return static_cast<MTLBackend *>(GPUBackend::get());
}
void samplers_update() override;
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override;
void compute_dispatch_indirect(StorageBuf *indirect_buf) override;

View file

@ -54,10 +54,6 @@ void MTLBackend::delete_resources()
MEM_delete(compiler_);
}
void MTLBackend::samplers_update() {
/* Placeholder -- Handled in MTLContext. */
};
Context *MTLBackend::context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext *ghost_context)
{
return new MTLContext(ghost_window, ghost_context);

View file

@ -1844,7 +1844,7 @@ void MTLContext::sampler_state_cache_init()
MTLSamplerMipFilterNotMipmapped;
descriptor.lodMinClamp = -1000;
descriptor.lodMaxClamp = 1000;
float aniso_filter = max_ff(16, U.anisotropic_filter);
float aniso_filter = min_ff(float(GPU_anisotropic_samples_get(filtering)), 16.0f);
descriptor.maxAnisotropy = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ? aniso_filter : 1;
descriptor.compareFunction = MTLCompareFunctionAlways;
descriptor.supportArgumentBuffers = true;

View file

@ -83,11 +83,6 @@ class GLBackend : public GPUBackend {
return static_cast<GLBackend *>(GPUBackend::get());
}
void samplers_update() override
{
GLTexture::samplers_update();
};
Context *context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext * /*ghost_context*/) override
{
return new GLContext(ghost_window, shared_orphan_list_);

View file

@ -569,6 +569,11 @@ void GLTexture::samplers_init()
{
glGenSamplers(samplers_state_cache_count_, &samplers_state_cache_[0][0][0]);
float max_anisotropy = 1.0f;
if (GLContext::texture_filter_anisotropic_support) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
}
for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
const GPUSamplerExtendMode extend_yz = static_cast<GPUSamplerExtendMode>(extend_yz_i);
const GLenum extend_t = to_gl(extend_yz);
@ -598,6 +603,16 @@ void GLTexture::samplers_init()
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, min_filter);
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, mag_filter);
if (GLContext::texture_filter_anisotropic_support &&
(filtering & GPU_SAMPLER_FILTERING_MIPMAP) &&
(filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK))
{
glSamplerParameterf(
sampler,
GL_TEXTURE_MAX_ANISOTROPY_EXT,
min_ff(float(GPU_anisotropic_samples_get(filtering)), max_anisotropy));
}
/* Other states are left to default:
* - GL_TEXTURE_BORDER_COLOR is {0, 0, 0, 0}.
* - GL_TEXTURE_MIN_LOD is -1000.
@ -611,7 +626,6 @@ void GLTexture::samplers_init()
}
}
}
samplers_update();
glGenSamplers(GPU_SAMPLER_CUSTOM_TYPES_COUNT, custom_samplers_state_cache_);
@ -636,34 +650,6 @@ void GLTexture::samplers_init()
debug::object_label(GL_SAMPLER, icon_sampler, "icons");
}
void GLTexture::samplers_update()
{
if (!GLContext::texture_filter_anisotropic_support) {
return;
}
float max_anisotropy = 1.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
const float anisotropic_filter = min_ff(max_anisotropy, U.anisotropic_filter);
for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
const GPUSamplerFiltering filtering = GPUSamplerFiltering(filtering_i);
if ((filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC) &&
(filtering & GPU_SAMPLER_FILTERING_MIPMAP))
{
glSamplerParameterf(samplers_state_cache_[extend_yz_i][extend_x_i][filtering_i],
GL_TEXTURE_MAX_ANISOTROPY_EXT,
anisotropic_filter);
}
}
}
}
}
void GLTexture::samplers_free()
{
glDeleteSamplers(samplers_state_cache_count_, &samplers_state_cache_[0][0][0]);

View file

@ -98,14 +98,6 @@ class GLTexture : public Texture {
*/
static void samplers_free();
/**
* Updates the anisotropic filter parameters of samplers that enables anisotropic filtering. This
* is not done as a one time initialization in samplers_init() method because the user might
* change the anisotropic filtering samples in the user preferences. So it is called in
* samplers_init() method as well as every time the user preferences change.
*/
static void samplers_update();
/**
* Get the handle of the OpenGL sampler that corresponds to the given sampler state.
* The sampler is retrieved from the cached samplers computed in the samplers_init() method.

View file

@ -563,14 +563,6 @@ void VKBackend::delete_resources()
MEM_delete(compiler_);
}
void VKBackend::samplers_update()
{
VKDevice &device = VKBackend::get().device;
if (device.is_initialized()) {
device.reinit();
}
}
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
VKContext &context = *VKContext::get();

View file

@ -56,7 +56,6 @@ class VKBackend : public GPUBackend {
void init_resources() override;
void delete_resources() override;
void samplers_update() override;
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override;
void compute_dispatch_indirect(StorageBuf *indirect_buf) override;

View file

@ -45,12 +45,15 @@ void VKSampler::create(const GPUSamplerState &sampler_state)
sampler_info.minLod = 0;
sampler_info.maxLod = 1000;
}
if ((sampler_state.filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC) &&
(U.anisotropic_filter > 1) &&
(device.physical_device_features_get().samplerAnisotropy == VK_TRUE))
if ((sampler_state.filtering & GPU_SAMPLER_FILTERING_MIPMAP) &&
(sampler_state.filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE) &&
device.physical_device_features_get().samplerAnisotropy == VK_TRUE)
{
float anisotropic_samples = min_ff(
float(GPU_anisotropic_samples_get(sampler_state.filtering)),
device.physical_device_properties_get().limits.maxSamplerAnisotropy);
sampler_info.anisotropyEnable = VK_TRUE;
sampler_info.maxAnisotropy = U.anisotropic_filter;
sampler_info.maxAnisotropy = anisotropic_samples;
}
}
else if (sampler_state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) {

View file

@ -998,10 +998,10 @@ struct RenderData {
/** Render engine. */
char engine[32] = "";
char _pad2[2] = {};
/** Performance Options. */
short perf_flag = 0;
short anisotropic_filter = 2;
/** Baking. */
struct BakeData bake;

View file

@ -6894,6 +6894,31 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem anisotropic_items[] = {
{1, "FILTER_0", 0, "Off", "Turn off anisotropic filtering"},
{2,
"FILTER_2",
0,
"2" BLI_STR_UTF8_MULTIPLICATION_SIGN,
"Use 2 samples for anisotropic filtering"},
{4,
"FILTER_4",
0,
"4" BLI_STR_UTF8_MULTIPLICATION_SIGN,
"Use 4 samples for anisotropic filtering"},
{8,
"FILTER_8",
0,
"8" BLI_STR_UTF8_MULTIPLICATION_SIGN,
"Use 8 samples for anisotropic filtering"},
{16,
"FILTER_16",
0,
"16" BLI_STR_UTF8_MULTIPLICATION_SIGN,
"Use 16 samples for anisotropic filtering"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem threads_mode_items[] = {
{0,
"AUTO",
@ -7235,6 +7260,13 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
"Use high quality tangent space at the cost of lower performance");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_mesh_quality_update");
prop = RNA_def_property(srna, "anisotropic_filter", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "anisotropic_filter");
RNA_def_property_enum_items(prop, anisotropic_items);
RNA_def_property_ui_text(
prop, "Anisotropic Filtering", "Quality of anisotropic filtering in materials");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
/* border */
prop = RNA_def_property(srna, "use_border", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "mode", R_BORDER);

View file

@ -750,12 +750,6 @@ static void rna_userdef_window_csd_params_update(Main *bmain, Scene *scene, Poin
rna_userdef_update(bmain, scene, ptr);
}
static void rna_userdef_anisotropic_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
GPU_samplers_update();
rna_userdef_update(bmain, scene, ptr);
}
static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
BKE_image_free_all_gputextures(bmain);
@ -6178,7 +6172,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, nullptr, "anisotropic_filter");
RNA_def_property_enum_items(prop, anisotropic_items);
RNA_def_property_ui_text(prop, "Anisotropic Filtering", "Quality of anisotropic filtering");
RNA_def_property_update(prop, 0, "rna_userdef_anisotropic_update");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "gl_texture_limit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "glreslimit");

View file

@ -48,7 +48,11 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
bNode *node_original = node->runtime->original ? node->runtime->original : node;
NodeTexImage *tex_original = static_cast<NodeTexImage *>(node_original->storage);
ImageUser *iuser = &tex_original->iuser;
GPUSamplerState sampler = {GPU_SAMPLER_FILTERING_LINEAR | GPU_SAMPLER_FILTERING_ANISOTROPIC,
/* Setting the GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE enables anisotropic filtering. The
* exact number of samples are being determined at bind time by the engine.
* See #blender::draw::PassBase<T>::material_set */
GPUSamplerState sampler = {GPU_SAMPLER_FILTERING_LINEAR |
GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE,
GPU_SAMPLER_EXTEND_MODE_REPEAT,
GPU_SAMPLER_EXTEND_MODE_REPEAT};
/* TODO(@fclem): For now assume mipmap is always enabled. */
@ -77,7 +81,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
/* Force the highest mipmap and don't do anisotropic filtering.
* This is to fix the artifact caused by derivatives discontinuity. */
sampler.disable_filtering_flag(GPU_SAMPLER_FILTERING_MIPMAP |
GPU_SAMPLER_FILTERING_ANISOTROPIC);
GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK);
}
else {
GPU_link(mat, "node_tex_environment_mirror_ball", in[0].link, &in[0].link);

View file

@ -87,8 +87,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
if (tex->interpolation != SHD_INTERP_CLOSEST) {
/* TODO(fclem): For now assume mipmap is always enabled. */
sampler_state.filtering = GPU_SAMPLER_FILTERING_ANISOTROPIC | GPU_SAMPLER_FILTERING_LINEAR |
GPU_SAMPLER_FILTERING_MIPMAP;
/* Setting the GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE enables anisotropic filtering. The
* exact number of samples are being determined at bind time by the engine.
* See #blender::draw::PassBase<T>::material_set */
sampler_state.filtering = GPU_SAMPLER_FILTERING_ANISOTROPIC_ENABLE |
GPU_SAMPLER_FILTERING_LINEAR | GPU_SAMPLER_FILTERING_MIPMAP;
}
const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART);