1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "SpirvShader.hpp"
16
17 #include "SamplerCore.hpp"
18 #include "Device/Config.hpp"
19 #include "System/Debug.hpp"
20 #include "System/Math.hpp"
21 #include "Vulkan/VkDescriptorSetLayout.hpp"
22 #include "Vulkan/VkDevice.hpp"
23 #include "Vulkan/VkImageView.hpp"
24 #include "Vulkan/VkSampler.hpp"
25
26 #include <spirv/unified1/spirv.hpp>
27
28 #include <climits>
29 #include <mutex>
30
31 namespace sw {
32
getImageSampler(const vk::Device * device,uint32_t signature,uint32_t samplerId,uint32_t imageViewId)33 SpirvEmitter::ImageSampler *SpirvEmitter::getImageSampler(const vk::Device *device, uint32_t signature, uint32_t samplerId, uint32_t imageViewId)
34 {
35 ImageInstructionSignature instruction(signature);
36 ASSERT(imageViewId != 0 && (samplerId != 0 || instruction.samplerMethod == Fetch || instruction.samplerMethod == Write));
37 ASSERT(device);
38
39 vk::Device::SamplingRoutineCache::Key key = { signature, samplerId, imageViewId };
40
41 auto createSamplingRoutine = [device](const vk::Device::SamplingRoutineCache::Key &key) {
42 ImageInstructionSignature instruction(key.instruction);
43 const vk::Identifier::State imageViewState = vk::Identifier(key.imageView).getState();
44 const vk::SamplerState *vkSamplerState = (key.sampler != 0) ? device->findSampler(key.sampler) : nullptr;
45
46 auto type = imageViewState.imageViewType;
47 auto samplerMethod = static_cast<SamplerMethod>(instruction.samplerMethod);
48
49 Sampler samplerState = {};
50 samplerState.textureType = type;
51 ASSERT(instruction.coordinates >= samplerState.dimensionality()); // "It may be a vector larger than needed, but all unused components appear after all used components."
52 samplerState.textureFormat = imageViewState.format;
53
54 samplerState.addressingModeU = convertAddressingMode(0, vkSamplerState, type);
55 samplerState.addressingModeV = convertAddressingMode(1, vkSamplerState, type);
56 samplerState.addressingModeW = convertAddressingMode(2, vkSamplerState, type);
57
58 samplerState.mipmapFilter = convertMipmapMode(vkSamplerState);
59 samplerState.swizzle = imageViewState.mapping;
60 samplerState.gatherComponent = instruction.gatherComponent;
61
62 if(vkSamplerState)
63 {
64 samplerState.textureFilter = convertFilterMode(vkSamplerState, type, samplerMethod);
65 samplerState.border = vkSamplerState->borderColor;
66 samplerState.customBorder = vkSamplerState->customBorderColor;
67
68 samplerState.mipmapFilter = convertMipmapMode(vkSamplerState);
69 samplerState.highPrecisionFiltering = vkSamplerState->highPrecisionFiltering;
70
71 samplerState.compareEnable = (vkSamplerState->compareEnable != VK_FALSE);
72 samplerState.compareOp = vkSamplerState->compareOp;
73 samplerState.unnormalizedCoordinates = (vkSamplerState->unnormalizedCoordinates != VK_FALSE);
74
75 samplerState.ycbcrModel = vkSamplerState->ycbcrModel;
76 samplerState.studioSwing = vkSamplerState->studioSwing;
77 samplerState.swappedChroma = vkSamplerState->swappedChroma;
78 samplerState.chromaFilter = vkSamplerState->chromaFilter == VK_FILTER_LINEAR ? FILTER_LINEAR : FILTER_POINT;
79 samplerState.chromaXOffset = vkSamplerState->chromaXOffset;
80 samplerState.chromaYOffset = vkSamplerState->chromaYOffset;
81
82 samplerState.mipLodBias = vkSamplerState->mipLodBias;
83 samplerState.maxAnisotropy = vkSamplerState->maxAnisotropy;
84 samplerState.minLod = vkSamplerState->minLod;
85 samplerState.maxLod = vkSamplerState->maxLod;
86
87 // If there's a single mip level and filtering doesn't depend on the LOD level,
88 // the sampler will need to compute the LOD to produce the proper result.
89 // Otherwise, it can be ignored.
90 // We can skip the LOD computation for all modes, except LOD query,
91 // where we have to return the proper value even if nothing else requires it.
92 if(imageViewState.singleMipLevel &&
93 (samplerState.textureFilter != FILTER_MIN_POINT_MAG_LINEAR) &&
94 (samplerState.textureFilter != FILTER_MIN_LINEAR_MAG_POINT) &&
95 (samplerMethod != Query))
96 {
97 samplerState.minLod = 0.0f;
98 samplerState.maxLod = 0.0f;
99 }
100 }
101 else if(samplerMethod == Fetch)
102 {
103 // OpImageFetch does not take a sampler descriptor, but for VK_EXT_image_robustness
104 // requires replacing invalid texels with zero.
105 // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
106 samplerState.border = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
107
108 // If there's a single mip level we can skip LOD computation.
109 if(imageViewState.singleMipLevel)
110 {
111 samplerState.minLod = 0.0f;
112 samplerState.maxLod = 0.0f;
113 }
114 // Otherwise make sure LOD is clamped for robustness
115 else
116 {
117 samplerState.minLod = imageViewState.minLod;
118 samplerState.maxLod = imageViewState.maxLod;
119 }
120 }
121 else if(samplerMethod == Write)
122 {
123 return emitWriteRoutine(instruction, samplerState);
124 }
125 else
126 ASSERT(false);
127
128 return emitSamplerRoutine(instruction, samplerState);
129 };
130
131 vk::Device::SamplingRoutineCache *cache = device->getSamplingRoutineCache();
132 auto routine = cache->getOrCreate(key, createSamplingRoutine);
133
134 return (ImageSampler *)(routine->getEntry());
135 }
136
emitWriteRoutine(ImageInstructionSignature instruction,const Sampler & samplerState)137 std::shared_ptr<rr::Routine> SpirvEmitter::emitWriteRoutine(ImageInstructionSignature instruction, const Sampler &samplerState)
138 {
139 // TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
140 rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
141 {
142 Pointer<Byte> descriptor = function.Arg<0>();
143 Pointer<SIMD::Float> coord = function.Arg<1>();
144 Pointer<SIMD::Float> texelAndMask = function.Arg<2>();
145 Pointer<Byte> constants = function.Arg<3>();
146
147 WriteImage(instruction, descriptor, coord, texelAndMask, samplerState.textureFormat);
148 }
149
150 return function("sampler");
151 }
152
emitSamplerRoutine(ImageInstructionSignature instruction,const Sampler & samplerState)153 std::shared_ptr<rr::Routine> SpirvEmitter::emitSamplerRoutine(ImageInstructionSignature instruction, const Sampler &samplerState)
154 {
155 // TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
156 rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
157 {
158 Pointer<Byte> texture = function.Arg<0>();
159 Pointer<SIMD::Float> in = function.Arg<1>();
160 Pointer<SIMD::Float> out = function.Arg<2>();
161 Pointer<Byte> constants = function.Arg<3>();
162
163 SIMD::Float uvwa[4];
164 SIMD::Float dRef;
165 SIMD::Float lodOrBias; // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
166 SIMD::Float dsx[4];
167 SIMD::Float dsy[4];
168 SIMD::Int offset[4];
169 SIMD::Int sampleId;
170 SamplerFunction samplerFunction = instruction.getSamplerFunction();
171
172 uint32_t i = 0;
173 for(; i < instruction.coordinates; i++)
174 {
175 uvwa[i] = in[i];
176 }
177
178 if(instruction.isDref())
179 {
180 dRef = in[i];
181 i++;
182 }
183
184 if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
185 {
186 lodOrBias = in[i];
187 i++;
188 }
189 else if(instruction.samplerMethod == Grad)
190 {
191 for(uint32_t j = 0; j < instruction.grad; j++, i++)
192 {
193 dsx[j] = in[i];
194 }
195
196 for(uint32_t j = 0; j < instruction.grad; j++, i++)
197 {
198 dsy[j] = in[i];
199 }
200 }
201
202 for(uint32_t j = 0; j < instruction.offset; j++, i++)
203 {
204 offset[j] = As<SIMD::Int>(in[i]);
205 }
206
207 if(instruction.sample)
208 {
209 sampleId = As<SIMD::Int>(in[i]);
210 }
211
212 SamplerCore s(constants, samplerState, samplerFunction);
213
214 // For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
215 // a single LOD per four elements, so we sample the image again for each LOD separately.
216 // TODO(b/133868964) Pass down 4 component lodOrBias, dsx, and dsy to sampleTexture
217 if(samplerFunction.method == Lod || samplerFunction.method == Grad ||
218 samplerFunction.method == Bias || samplerFunction.method == Fetch)
219 {
220 // Only perform per-lane sampling if LOD diverges or we're doing Grad sampling.
221 Bool perLaneSampling = (samplerFunction.method == Grad) || Divergent(As<SIMD::Int>(lodOrBias));
222 auto lod = Pointer<Float>(&lodOrBias);
223 Int i = 0;
224 Do
225 {
226 SIMD::Float dPdx;
227 SIMD::Float dPdy;
228 dPdx.x = Pointer<Float>(&dsx[0])[i];
229 dPdx.y = Pointer<Float>(&dsx[1])[i];
230 dPdx.z = Pointer<Float>(&dsx[2])[i];
231
232 dPdy.x = Pointer<Float>(&dsy[0])[i];
233 dPdy.y = Pointer<Float>(&dsy[1])[i];
234 dPdy.z = Pointer<Float>(&dsy[2])[i];
235
236 SIMD::Float4 sample = s.sampleTexture(texture, uvwa, dRef, lod[i], dPdx, dPdy, offset, sampleId);
237
238 If(perLaneSampling)
239 {
240 Pointer<Float> rgba = out;
241 rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
242 rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
243 rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
244 rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
245 i++;
246 }
247 Else
248 {
249 Pointer<SIMD::Float> rgba = out;
250 rgba[0] = sample.x;
251 rgba[1] = sample.y;
252 rgba[2] = sample.z;
253 rgba[3] = sample.w;
254 i = SIMD::Width;
255 }
256 }
257 Until(i == SIMD::Width);
258 }
259 else
260 {
261 Float lod = Float(lodOrBias.x);
262 SIMD::Float4 sample = s.sampleTexture(texture, uvwa, dRef, lod, (dsx[0]), (dsy[0]), offset, sampleId);
263
264 Pointer<SIMD::Float> rgba = out;
265 rgba[0] = sample.x;
266 rgba[1] = sample.y;
267 rgba[2] = sample.z;
268 rgba[3] = sample.w;
269 }
270 }
271
272 return function("sampler");
273 }
274
convertFilterMode(const vk::SamplerState * samplerState,VkImageViewType imageViewType,SamplerMethod samplerMethod)275 sw::FilterType SpirvEmitter::convertFilterMode(const vk::SamplerState *samplerState, VkImageViewType imageViewType, SamplerMethod samplerMethod)
276 {
277 if(samplerMethod == Gather)
278 {
279 return FILTER_GATHER;
280 }
281
282 if(samplerMethod == Fetch)
283 {
284 return FILTER_POINT;
285 }
286
287 if(samplerState->anisotropyEnable != VK_FALSE)
288 {
289 if(imageViewType == VK_IMAGE_VIEW_TYPE_2D || imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
290 {
291 if(samplerMethod != Lod) // TODO(b/162926129): Support anisotropic filtering with explicit LOD.
292 {
293 return FILTER_ANISOTROPIC;
294 }
295 }
296 }
297
298 switch(samplerState->magFilter)
299 {
300 case VK_FILTER_NEAREST:
301 switch(samplerState->minFilter)
302 {
303 case VK_FILTER_NEAREST: return FILTER_POINT;
304 case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
305 default:
306 UNSUPPORTED("minFilter %d", samplerState->minFilter);
307 return FILTER_POINT;
308 }
309 break;
310 case VK_FILTER_LINEAR:
311 switch(samplerState->minFilter)
312 {
313 case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
314 case VK_FILTER_LINEAR: return FILTER_LINEAR;
315 default:
316 UNSUPPORTED("minFilter %d", samplerState->minFilter);
317 return FILTER_POINT;
318 }
319 break;
320 default:
321 break;
322 }
323
324 UNSUPPORTED("magFilter %d", samplerState->magFilter);
325 return FILTER_POINT;
326 }
327
convertMipmapMode(const vk::SamplerState * samplerState)328 sw::MipmapType SpirvEmitter::convertMipmapMode(const vk::SamplerState *samplerState)
329 {
330 if(!samplerState)
331 {
332 return MIPMAP_POINT; // Samplerless operations (OpImageFetch) can take an integer Lod operand.
333 }
334
335 if(samplerState->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
336 {
337 // TODO(b/151263485): Check image view level count instead.
338 return MIPMAP_NONE;
339 }
340
341 switch(samplerState->mipmapMode)
342 {
343 case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
344 case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
345 default:
346 UNSUPPORTED("mipmapMode %d", samplerState->mipmapMode);
347 return MIPMAP_POINT;
348 }
349 }
350
convertAddressingMode(int coordinateIndex,const vk::SamplerState * samplerState,VkImageViewType imageViewType)351 sw::AddressingMode SpirvEmitter::convertAddressingMode(int coordinateIndex, const vk::SamplerState *samplerState, VkImageViewType imageViewType)
352 {
353 switch(imageViewType)
354 {
355 case VK_IMAGE_VIEW_TYPE_1D:
356 case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
357 if(coordinateIndex >= 1)
358 {
359 return ADDRESSING_UNUSED;
360 }
361 break;
362 case VK_IMAGE_VIEW_TYPE_2D:
363 case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
364 if(coordinateIndex == 2)
365 {
366 return ADDRESSING_UNUSED;
367 }
368 break;
369
370 case VK_IMAGE_VIEW_TYPE_3D:
371 break;
372
373 case VK_IMAGE_VIEW_TYPE_CUBE:
374 case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
375 if(coordinateIndex <= 1) // Cube faces themselves are addressed as 2D images.
376 {
377 // Vulkan 1.1 spec:
378 // "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
379 // VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE is used, and if VK_FILTER_LINEAR is used within a mip level then sampling at the edges
380 // is performed as described earlier in the Cube map edge handling section."
381 // This corresponds with our 'SEAMLESS' addressing mode.
382 return ADDRESSING_SEAMLESS;
383 }
384 else // coordinateIndex == 2
385 {
386 // The cube face is an index into 2D array layers.
387 return ADDRESSING_CUBEFACE;
388 }
389 break;
390
391 default:
392 UNSUPPORTED("imageViewType %d", imageViewType);
393 return ADDRESSING_WRAP;
394 }
395
396 if(!samplerState)
397 {
398 // OpImageFetch does not take a sampler descriptor, but still needs a valid
399 // addressing mode that prevents out-of-bounds accesses:
400 // "The value returned by a read of an invalid texel is undefined, unless that
401 // read operation is from a buffer resource and the robustBufferAccess feature
402 // is enabled. In that case, an invalid texel is replaced as described by the
403 // robustBufferAccess feature." - Vulkan 1.1
404
405 // VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
406 // ADDRESSING_BORDER causes texel replacement to be performed.
407 // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
408 return ADDRESSING_BORDER;
409 }
410
411 VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
412 switch(coordinateIndex)
413 {
414 case 0: addressMode = samplerState->addressModeU; break;
415 case 1: addressMode = samplerState->addressModeV; break;
416 case 2: addressMode = samplerState->addressModeW; break;
417 default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
418 }
419
420 switch(addressMode)
421 {
422 case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
423 case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
424 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
425 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
426 case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
427 default:
428 UNSUPPORTED("addressMode %d", addressMode);
429 return ADDRESSING_WRAP;
430 }
431 }
432
433 } // namespace sw
434