Repository navigation
Conversation
Format 43 packs R as a 10-bit and G and B as 11-bit unsigned floats, which Vulkan has no format for. Keep it in an R32_UINT image like the other packed formats without a host equivalent, and convert texels in the shader on image loads and stores; sampling it still throws.
Images kept in an R32_UINT view (10/11-bit unorm and float formats) cannot be filtered by the host sampler, so sampling them threw. Fetch and unpack the texels in the shader and filter them there: point or bilinear, with wrap, mirror, clamp-to-edge, clamp-to-half-border or clamp-to-border addressing and the built-in border colors, on single-level images, with _lz, or with an explicit LOD selecting or blending mip levels. These images no longer need a point-filtered copy of their sampler.
…edFilter Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Unnormalized coordinates are in texels of the base level, so the in-shader filter uses them without scaling by the image size and samples level 0. Images filtered in the shader are left out of the unnormalized sampler checks, which only apply to host-sampled views.
Volumes of converted formats are sampled like the 2D images: the in-shader filter takes the sampler's Z addressing mode, fetches the two nearest slices and blends them by the W weight (or picks one slice with point filtering).
adriadam10
left a comment
There was a problem hiding this comment.
Measured on gfx1035 hardware with the HW oracle (format 43, image_sample_lz/image_sample_l, IEEE=0). Build and ctest pass after a local rebase, and the test fails without the change. 2D wrap/mirror/clamp-to-edge/half-border/border addressing and point sampling match the hardware bit for bit at 1/64-texel steps. These parts don't match:
ResourceMaterializer.cpp:605: the mip filter is read from S# word2 bits 24-25, which holdZ_FILTER.MIP_FILTERis bits 26-27 (seeGuestSamplerResource.cpp:84on main). On a two-level image withimage_sample_l, bits 24-25 change nothing.MIP_FILTER=1 switches to level 1 at LOD 0.5, andMIP_FILTER=2 blends. With this PR, a point or linear mip sampler whoseZ_FILTERis 0 samples the base level only.ResourceMaterializer.cpp:598/SpirvImageEmitter.cpp:1329:Z_FILTERis not modelled for 3D views. On the hardware, 0 follows the XY filter, 1 is point and 2 is linear. With bilinear XY andZ_FILTER=1, the hardware picks one slice (G = 0, 0, 10, 10 at w = 0.25..0.625 between slices 0 and 10) where the PR blends them. Point XY withZ_FILTER=2 is the opposite case. Either decode it or throw for 1/2.SpirvImageEmitter.cpp:1275: the border colour does go throughdst_selon AMD. With swizzle XYZ1 and a transparent black border, the hardware returns (0, 0, 0, 1) and the PR returns (0, 0, 0, 0). With X=ZERO and an opaque white border, the hardware returns 0 in X. The description's claim that AMD does not swizzle border colours doesn't hold. Apply the view swizzle to the border value, at least the constant selectors.SpirvImageEmitter.cpp:1313-1314, 1344: the hardware quantizes the bilinear weights to 1/256, rounding to nearest with ties to even (w = 1.5/256 → 2/256, 2.5/256 → 2/256, 3.5/256 → 4/256). The mip-blend weight is quantized the same way (LOD 0.49 → 125/256). Example: texel coordinate 3.00293 gives 0.49609375 on the hardware and 0.49707031 here. In a 1/1024-texel sweep over one texel, 768 of 1024 rows differ.SpirvImageEmitter.cpp:1303-1305:TRUNC_COORD(S# word0 bit 27) is ignored. With it set, the hardware matchesfloor. With it clear (0), the hardware reads texel k at t = k − 2^-12 and t = k − 2^-20, wherefloorgives k − 1. Model the clear case, or throw for it.- Test: every sample sits on a quarter texel, which is exactly on the 1/256 grid, and the tolerance is 1e-5, so the test can't catch the weight issue. Please add off-grid positions, a mip-filter case and a border case with a constant selector, with expected values pinned exactly.
- It also conflicts with main now:
FormatVersionis already 9 there, so this needs 10.
adriadam10
left a comment
There was a problem hiding this comment.
Thanks for the update. This can't be rebased mechanically any more, so it needs a port rather than a conflict fix.
After #639, main has no ResourceSpecialization and no per-resource emulatedCompare in the IR. The emulated depth comparison is now driven by pipeline specialization constants (PipelineSpecialization::CompareBase in DescriptorBindingBuilder.cpp) and by the image modes enumerated in ResourceMaterializer (info.runtimeImageModes, RuntimeImageMode). Applying your three commits on top of main conflicts in ResourceMaterializer.{hpp,cpp}, DescriptorBindingBuilder.cpp and ShaderDiskCache.{hpp,cpp}, all in that redesign.
What the port needs:
- Add the filter as a runtime image mode for the converted formats (30 and 43) and pass the sampler state (addressing, border, mip filter, Z filter) as specialization words, the way the compare state is passed, instead of
ResourceSpecialization::Image::emulatedFilter. ShaderDiskCache::FormatVersionis 26 onmain, so bump to 27 (or max of main and open PRs + 1).
The points from the previous review still apply, measured on gfx1035 with the HW oracle (format 43, image_sample_lz/image_sample_l, IEEE=0):
- Mip filter is read from S# word2 bits 24-25 (
Z_FILTER);MIP_FILTERis bits 26-27 (GuestSamplerResource.cpp:84). Z_FILTERis not modelled for 3D views: 0 follows the XY filter, 1 is point, 2 is linear. Decode it or throw for 1/2.- The border colour goes through
dst_selon AMD (XYZ1 with transparent black returns (0, 0, 0, 1)); apply the view swizzle to the border value, at least the constant selectors. - Bilinear and mip-blend weights are quantized to 1/256, round to nearest even (1.5/256 -> 2/256, 2.5/256 -> 2/256, 3.5/256 -> 4/256).
TRUNC_COORD(S# word0 bit 27) is ignored: model the clear case or throw for it.- The test only samples on quarter texels (already on the 1/256 grid) with 1e-5 tolerance. Add off-grid positions, a mip-filter case and a border case with a constant selector, with exact expected values.
Happy to look again once it is on the new pipeline.
|
Closing: the points from 10-08 are still open, and since #639 this needs a port onto main's runtime image modes rather than a rebase. If you decide to continue, please reopen. |
What
Images whose format has no host equivalent are kept in an
R32_UINTimage and converted in the shader: 10/11-bit unorm (format 30) and, with #1394, R10_G11_B11 float (format 43). The host sampler cannot filter them, so sampling them threw ("samples or gathers a converted unorm/float image, which needs filtering in the shader and is not implemented"). A title samples R10_G11_B11 float images from several compute shaders.This filters those images in the shader:
The materializer decodes the paired sampler into a new
EmulatedFilterstate on the image:The state is part of the specialization and the disk-cache entry, and
FormatVersiongoes from 7 to 8.The emitter fetches the four (or one) texels with
OpImageFetch, unpacks them with the existing converted-image path (UnpackImageTexel, including the view swizzle) and blends them.VK_EXT_border_color_swizzledescribes.Levels:
_lzsamples and a sampler without a mip filter use the base level.image_sample_l) is clamped to the image's levels. The mip filter then selects the nearest level (point) or blends the two adjacent levels (linear).Images filtered this way no longer get a point-filtered copy of their sampler, since the shader never samples through it.
Unnormalized sampler coordinates are used as texel positions on the base level, without scaling by the image size. Images filtered this way are left out of the unnormalized sampler checks, which only apply to host-sampled views.
Still refused:
Tested
Windows 11, MinGW-w64 GCC 15.2.0, RTX 5090:
agc_driver_image_converted_unormpasses with new cases.image_sample_lzacross a 32-texel R10_G11_B11 float image, sampled a quarter texel left of each centre, matches a CPU reference for:Gathers of such an image, and mirror-once addressing, are refused.
A title whose compute shaders sample R10_G11_B11 float images (bilinear, with wrap, mirror, clamp-to-edge and half-border samplers, including an explicit-LOD sample of a two-level image) now gets past them.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)