diff --git a/src/gallium/auxiliary/gallivm/lp_bld_jit_types.c b/src/gallium/auxiliary/gallivm/lp_bld_jit_types.c index 6b4f6b42719..94159b97f4b 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_jit_types.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_jit_types.c @@ -421,6 +421,29 @@ lp_build_llvm_texture_member(struct gallivm_state *gallivm, return res; } +static LLVMValueRef +lp_build_llvm_texture_view_min_lod(struct gallivm_state *gallivm, + LLVMTypeRef resources_type, + LLVMValueRef resources_ptr, + unsigned texture_unit, + LLVMValueRef texture_unit_offset) +{ + /* Only reached in bindless mode, where apply_view_min_lod gates the call. */ + assert(gallivm->texture_descriptor); + + LLVMBuilderRef builder = gallivm->builder; + LLVMTypeRef float_type = LLVMFloatTypeInContext(gallivm->context); + + LLVMValueRef ptr = LLVMBuildAdd(builder, gallivm->texture_descriptor, + lp_build_const_int64(gallivm, + offsetof(struct lp_image_descriptor, texture.view_min_lod)), + ""); + ptr = LLVMBuildIntToPtr(builder, ptr, LLVMPointerType(float_type, 0), ""); + + return LLVMBuildLoad2(builder, float_type, ptr, "view_min_lod"); +} + + static LLVMValueRef lp_build_llvm_texture_residency(struct gallivm_state *gallivm, LLVMTypeRef resources_type, @@ -725,6 +748,8 @@ lp_build_jit_fill_sampler_dynamic_state(struct lp_sampler_dynamic_state *state) state->max_lod = lp_build_llvm_sampler_max_lod; state->lod_bias = lp_build_llvm_sampler_lod_bias; state->border_color = lp_build_llvm_sampler_border_color; + + state->view_min_lod = lp_build_llvm_texture_view_min_lod; } void diff --git a/src/gallium/auxiliary/gallivm/lp_bld_jit_types.h b/src/gallium/auxiliary/gallivm/lp_bld_jit_types.h index 4456fb1992e..4e349fee437 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_jit_types.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_jit_types.h @@ -238,6 +238,7 @@ struct lp_jit_bindless_texture const void *base; const void *residency; uint32_t base_offset; + float view_min_lod; /* VK_EXT_image_view_min_lod, relative to first_level */ }; struct lp_image_descriptor { diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample.c b/src/gallium/auxiliary/gallivm/lp_bld_sample.c index 4105afc7eeb..14c882949ed 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_sample.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_sample.c @@ -133,6 +133,13 @@ lp_sampler_static_texture_state(struct lp_static_texture_state *state, if (state->tiled) state->tiled_samples = texture->nr_samples; + /* VK_EXT_image_view_min_lod: bake "apply view min-lod" into the static state + * so views without a clamp don't pay any shader cost. The actual clamp value + * lives in the texture descriptor (dynamic state). + */ + state->apply_view_min_lod = texture->target != PIPE_BUFFER && + view->u.tex.min_lod_clamp > 0.0f; + /* * the layer / element / level parameters are all either dynamic * state or handled transparently wrt execution. @@ -1158,6 +1165,16 @@ lp_build_lod_selector(struct lp_build_sample_context *bld, bld->resources_ptr, sampler_unit); lod = lp_build_broadcast_scalar(lodf_bld, min_lod); + + /* VK_EXT_image_view_min_lod applies even when min == max lod. */ + if (bld->static_texture_state->apply_view_min_lod && + dynamic_state->view_min_lod) { + LLVMValueRef view_min_lod = + dynamic_state->view_min_lod(bld->gallivm, bld->resources_type, + bld->resources_ptr, sampler_unit, NULL); + view_min_lod = lp_build_broadcast_scalar(lodf_bld, view_min_lod); + lod = lp_build_max(lodf_bld, lod, view_min_lod); + } } else { if (explicit_lod) { if (bld->num_lods != bld->coord_type.length) { @@ -1274,6 +1291,17 @@ lp_build_lod_selector(struct lp_build_sample_context *bld, lod = lp_build_max(lodf_bld, lod, desc_min_lod); } + /* VK_EXT_image_view_min_lod: clamp lod to the per-view minimum. */ + if (bld->static_texture_state->apply_view_min_lod && + dynamic_state->view_min_lod) { + LLVMValueRef view_min_lod = + dynamic_state->view_min_lod(bld->gallivm, bld->resources_type, + bld->resources_ptr, sampler_unit, NULL); + view_min_lod = lp_build_broadcast_scalar(lodf_bld, view_min_lod); + + lod = lp_build_max(lodf_bld, lod, view_min_lod); + } + if (min_lod) { if (bld->num_lods != bld->coord_type.length) { min_lod = lp_build_pack_aos_scalars(bld->gallivm, bld->coord_bld.type, diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample.h b/src/gallium/auxiliary/gallivm/lp_bld_sample.h index 59e16a4279a..14b23147b35 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_sample.h +++ b/src/gallium/auxiliary/gallivm/lp_bld_sample.h @@ -221,6 +221,8 @@ struct lp_static_texture_state unsigned level_zero_only:1; unsigned tiled:1; unsigned tiled_samples:5; + /**< view min lod clamp (VK_EXT_image_view_min_lod): apply lod = max(lod, view_min_lod) */ + unsigned apply_view_min_lod:1; }; @@ -377,6 +379,13 @@ struct lp_sampler_dynamic_state LLVMValueRef resources_ptr, unsigned sampler_unit); + /* Obtain the per-view min-lod clamp (returns float, relative to first_level) */ + LLVMValueRef + (*view_min_lod)(struct gallivm_state *gallivm, + LLVMTypeRef resources_type, + LLVMValueRef resources_ptr, + unsigned texture_unit, LLVMValueRef texture_unit_offset); + /** * Obtain texture cache (returns ptr to lp_build_format_cache). * diff --git a/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c b/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c index 5189caa1059..559079c7859 100644 --- a/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c +++ b/src/gallium/auxiliary/gallivm/lp_bld_sample_soa.c @@ -2969,10 +2969,35 @@ lp_build_fetch_texel(struct lp_build_sample_context *bld, first_level = lp_build_broadcast_scalar(&bld->leveli_bld, first_level); last_level = lp_build_broadcast_scalar(&bld->leveli_bld, last_level); + + LLVMValueRef requested_level = ilevel; lp_build_nearest_mip_level(bld, first_level, last_level, ilevel, &ilevel, out_of_bound_ret_zero ? &out_of_bounds : NULL); + + /* The Vulkan spec defines an OpImageFetch with LOD below the view's + * minLodInteger as reading zero. + * Since view_min_lod is view-relative, clamp against its floor. + */ + if (out_of_bound_ret_zero && + bld->static_texture_state->apply_view_min_lod && + bld->dynamic_state->view_min_lod) { + LLVMValueRef vml = + bld->dynamic_state->view_min_lod(bld->gallivm, bld->resources_type, + bld->resources_ptr, texture_unit, NULL); + LLVMValueRef min_level = + lp_build_broadcast_scalar(&bld->leveli_bld, + lp_build_ifloor(&bld->float_bld, vml)); + LLVMValueRef below = lp_build_cmp(&bld->leveli_bld, PIPE_FUNC_LESS, + requested_level, min_level); + if (bld->num_mips == 1) + below = lp_build_broadcast_scalar(&bld->int_coord_bld, below); + else if (bld->num_mips != bld->coord_bld.type.length) + below = lp_build_unpack_broadcast_aos_scalars(bld->gallivm, + bld->leveli_bld.type, bld->int_coord_bld.type, below); + out_of_bounds = lp_build_or(int_coord_bld, out_of_bounds, below); + } } else { assert(bld->num_mips == 1); if (bld->static_texture_state->target != PIPE_BUFFER) { diff --git a/src/gallium/drivers/llvmpipe/lp_jit.c b/src/gallium/drivers/llvmpipe/lp_jit.c index 71e77b77cf6..8d39f7014de 100644 --- a/src/gallium/drivers/llvmpipe/lp_jit.c +++ b/src/gallium/drivers/llvmpipe/lp_jit.c @@ -515,8 +515,14 @@ lp_jit_bindless_texture_from_pipe(struct lp_jit_bindless_texture *jit, const str assert(!lp_tex->dt); if (llvmpipe_resource_is_texture(res)) { + /* Convert the view's min_lod_clamp to view-relative space for the shader. */ + float view_relative_min_lod = + view->u.tex.min_lod_clamp - (float)view->u.tex.first_level; + jit->view_min_lod = view_relative_min_lod > 0.0f ? view_relative_min_lod : 0.0f; + jit->base = lp_tex->tex_data; } else { + jit->view_min_lod = 0.0f; jit->base = lp_tex->data; } const void *base = jit->base; @@ -586,6 +592,7 @@ void lp_jit_bindless_texture_buffer_from_bda(struct lp_jit_bindless_texture *jit, void *mem) { jit->base = mem; + jit->view_min_lod = 0.0f; } void diff --git a/src/gallium/frontends/lavapipe/ci/lvp-vkd3d-fails.txt b/src/gallium/frontends/lavapipe/ci/lvp-vkd3d-fails.txt index ce37bf5def0..1fabe753e9c 100644 --- a/src/gallium/frontends/lavapipe/ci/lvp-vkd3d-fails.txt +++ b/src/gallium/frontends/lavapipe/ci/lvp-vkd3d-fails.txt @@ -8,7 +8,6 @@ test_planar_video_formats,Fail test_sample_instructions,Fail test_sampler_rounding,Fail -test_view_min_lod,Fail test_null_descriptor_resinfo_dxbc,Fail test_null_descriptor_resinfo_dxil,Fail diff --git a/src/gallium/frontends/lavapipe/lvp_device.c b/src/gallium/frontends/lavapipe/lvp_device.c index a289be96c59..775d37a2eb0 100644 --- a/src/gallium/frontends/lavapipe/lvp_device.c +++ b/src/gallium/frontends/lavapipe/lvp_device.c @@ -255,6 +255,7 @@ static const struct vk_device_extension_table lvp_device_extensions_supported = .EXT_image_2d_view_of_3d = true, .EXT_image_sliced_view_of_3d = true, .EXT_image_robustness = true, + .EXT_image_view_min_lod = true, .EXT_index_type_uint8 = true, .EXT_inline_uniform_block = true, .EXT_load_store_op_none = true, @@ -635,6 +636,9 @@ lvp_get_features(const struct lvp_physical_device *pdevice, /* VK_EXT_image_sliced_view_of_3d */ .imageSlicedViewOf3D = true, + /* VK_EXT_image_view_min_lod */ + .minLod = true, + /* VK_EXT_depth_bias_control */ .depthBiasControl = true, .leastRepresentableValueForceUnormRepresentation = true, diff --git a/src/gallium/frontends/lavapipe/lvp_image.c b/src/gallium/frontends/lavapipe/lvp_image.c index 461d3396bc8..44d457fd454 100644 --- a/src/gallium/frontends/lavapipe/lvp_image.c +++ b/src/gallium/frontends/lavapipe/lvp_image.c @@ -311,6 +311,7 @@ lvp_create_samplerview(struct pipe_context *pctx, struct lvp_image_view *iv, VkF templ.u.tex.last_layer = iv->vk.base_array_layer + iv->vk.layer_count - 1; templ.u.tex.first_level = iv->vk.base_mip_level; templ.u.tex.last_level = iv->vk.base_mip_level + iv->vk.level_count - 1; + templ.u.tex.min_lod_clamp = iv->vk.min_lod; templ.swizzle_r = vk_conv_swizzle(iv->vk.swizzle.r, PIPE_SWIZZLE_X); templ.swizzle_g = vk_conv_swizzle(iv->vk.swizzle.g, PIPE_SWIZZLE_Y); templ.swizzle_b = vk_conv_swizzle(iv->vk.swizzle.b, PIPE_SWIZZLE_Z);