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" // TODO: Figure out what's needed.
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(uint32_t inst,vk::SampledImageDescriptor const * imageDescriptor,const vk::Sampler * sampler)33 SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, vk::SampledImageDescriptor const *imageDescriptor, const vk::Sampler *sampler)
34 {
35 ImageInstruction instruction(inst);
36 const auto samplerId = sampler ? sampler->id : 0;
37 ASSERT(imageDescriptor->imageViewId != 0 && (samplerId != 0 || instruction.samplerMethod == Fetch));
38 ASSERT(imageDescriptor->device);
39
40 vk::Device::SamplingRoutineCache::Key key = { inst, imageDescriptor->imageViewId, samplerId };
41
42 vk::Device::SamplingRoutineCache *cache = imageDescriptor->device->getSamplingRoutineCache();
43
44 auto createSamplingRoutine = [&](const vk::Device::SamplingRoutineCache::Key &key) {
45 auto type = imageDescriptor->type;
46
47 Sampler samplerState = {};
48 samplerState.textureType = type;
49 samplerState.textureFormat = imageDescriptor->format;
50
51 samplerState.addressingModeU = convertAddressingMode(0, sampler, type);
52 samplerState.addressingModeV = convertAddressingMode(1, sampler, type);
53 samplerState.addressingModeW = convertAddressingMode(2, sampler, type);
54
55 samplerState.mipmapFilter = convertMipmapMode(sampler);
56 samplerState.swizzle = imageDescriptor->swizzle;
57 samplerState.gatherComponent = instruction.gatherComponent;
58
59 if(sampler)
60 {
61 samplerState.textureFilter = convertFilterMode(sampler, type, instruction);
62 samplerState.border = sampler->borderColor;
63
64 samplerState.mipmapFilter = convertMipmapMode(sampler);
65 samplerState.highPrecisionFiltering = (sampler->filteringPrecision == VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE);
66
67 samplerState.compareEnable = (sampler->compareEnable != VK_FALSE);
68 samplerState.compareOp = sampler->compareOp;
69 samplerState.unnormalizedCoordinates = (sampler->unnormalizedCoordinates != VK_FALSE);
70
71 samplerState.ycbcrModel = sampler->ycbcrModel;
72 samplerState.studioSwing = sampler->studioSwing;
73 samplerState.swappedChroma = sampler->swappedChroma;
74
75 samplerState.mipLodBias = sampler->mipLodBias;
76 samplerState.maxAnisotropy = sampler->maxAnisotropy;
77 samplerState.minLod = sampler->minLod;
78 samplerState.maxLod = sampler->maxLod;
79 }
80 else
81 {
82 // OpImageFetch does not take a sampler descriptor, but for VK_EXT_image_robustness
83 // requires replacing invalid texels with zero.
84 // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
85 samplerState.border = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
86 }
87
88 return emitSamplerRoutine(instruction, samplerState);
89 };
90
91 auto routine = cache->getOrCreate(key, createSamplingRoutine);
92
93 return (ImageSampler *)(routine->getEntry());
94 }
95
emitSamplerRoutine(ImageInstruction instruction,const Sampler & samplerState)96 std::shared_ptr<rr::Routine> SpirvShader::emitSamplerRoutine(ImageInstruction instruction, const Sampler &samplerState)
97 {
98 // TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
99 rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
100 {
101 Pointer<Byte> texture = function.Arg<0>();
102 Pointer<SIMD::Float> in = function.Arg<1>();
103 Pointer<SIMD::Float> out = function.Arg<2>();
104 Pointer<Byte> constants = function.Arg<3>();
105
106 SIMD::Float uvwa[4];
107 SIMD::Float dRef;
108 SIMD::Float lodOrBias; // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
109 Vector4f dsx;
110 Vector4f dsy;
111 Vector4i offset;
112 SIMD::Int sampleId;
113 SamplerFunction samplerFunction = instruction.getSamplerFunction();
114
115 uint32_t i = 0;
116 for(; i < instruction.coordinates; i++)
117 {
118 uvwa[i] = in[i];
119 }
120
121 if(instruction.isDref())
122 {
123 dRef = in[i];
124 i++;
125 }
126
127 if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
128 {
129 lodOrBias = in[i];
130 i++;
131 }
132 else if(instruction.samplerMethod == Grad)
133 {
134 for(uint32_t j = 0; j < instruction.grad; j++, i++)
135 {
136 dsx[j] = in[i];
137 }
138
139 for(uint32_t j = 0; j < instruction.grad; j++, i++)
140 {
141 dsy[j] = in[i];
142 }
143 }
144
145 for(uint32_t j = 0; j < instruction.offset; j++, i++)
146 {
147 offset[j] = As<SIMD::Int>(in[i]);
148 }
149
150 if(instruction.sample)
151 {
152 sampleId = As<SIMD::Int>(in[i]);
153 }
154
155 SamplerCore s(constants, samplerState);
156
157 // For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
158 // a single LOD per four elements, so we sample the image again for each LOD separately.
159 if(samplerFunction.method == Lod || samplerFunction.method == Grad) // TODO(b/133868964): Also handle divergent Bias and Fetch with Lod.
160 {
161 auto lod = Pointer<Float>(&lodOrBias);
162
163 For(Int i = 0, i < SIMD::Width, i++)
164 {
165 SIMD::Float dPdx;
166 SIMD::Float dPdy;
167
168 dPdx.x = Pointer<Float>(&dsx.x)[i];
169 dPdx.y = Pointer<Float>(&dsx.y)[i];
170 dPdx.z = Pointer<Float>(&dsx.z)[i];
171
172 dPdy.x = Pointer<Float>(&dsy.x)[i];
173 dPdy.y = Pointer<Float>(&dsy.y)[i];
174 dPdy.z = Pointer<Float>(&dsy.z)[i];
175
176 Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lod[i], dPdx, dPdy, offset, sampleId, samplerFunction);
177
178 Pointer<Float> rgba = out;
179 rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
180 rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
181 rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
182 rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
183 }
184 }
185 else
186 {
187 Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lodOrBias.x, (dsx.x), (dsy.x), offset, sampleId, samplerFunction);
188
189 Pointer<SIMD::Float> rgba = out;
190 rgba[0] = sample.x;
191 rgba[1] = sample.y;
192 rgba[2] = sample.z;
193 rgba[3] = sample.w;
194 }
195 }
196
197 return function("sampler");
198 }
199
convertFilterMode(const vk::Sampler * sampler,VkImageViewType imageViewType,ImageInstruction instruction)200 sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler, VkImageViewType imageViewType, ImageInstruction instruction)
201 {
202 if(instruction.samplerMethod == Gather)
203 {
204 return FILTER_GATHER;
205 }
206
207 if(instruction.samplerMethod == Fetch)
208 {
209 return FILTER_POINT;
210 }
211
212 if(sampler->anisotropyEnable != VK_FALSE)
213 {
214 if(imageViewType == VK_IMAGE_VIEW_TYPE_2D || imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
215 {
216 if(instruction.samplerMethod != Lod) // TODO(b/162926129): Support anisotropic filtering with explicit LOD.
217 {
218 return FILTER_ANISOTROPIC;
219 }
220 }
221 }
222
223 switch(sampler->magFilter)
224 {
225 case VK_FILTER_NEAREST:
226 switch(sampler->minFilter)
227 {
228 case VK_FILTER_NEAREST: return FILTER_POINT;
229 case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
230 default:
231 UNSUPPORTED("minFilter %d", sampler->minFilter);
232 return FILTER_POINT;
233 }
234 break;
235 case VK_FILTER_LINEAR:
236 switch(sampler->minFilter)
237 {
238 case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
239 case VK_FILTER_LINEAR: return FILTER_LINEAR;
240 default:
241 UNSUPPORTED("minFilter %d", sampler->minFilter);
242 return FILTER_POINT;
243 }
244 break;
245 default:
246 break;
247 }
248
249 UNSUPPORTED("magFilter %d", sampler->magFilter);
250 return FILTER_POINT;
251 }
252
convertMipmapMode(const vk::Sampler * sampler)253 sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
254 {
255 if(!sampler)
256 {
257 return MIPMAP_POINT; // Samplerless operations (OpImageFetch) can take an integer Lod operand.
258 }
259
260 if(sampler->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
261 {
262 // TODO(b/151263485): Check image view level count instead.
263 return MIPMAP_NONE;
264 }
265
266 switch(sampler->mipmapMode)
267 {
268 case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
269 case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
270 default:
271 UNSUPPORTED("mipmapMode %d", sampler->mipmapMode);
272 return MIPMAP_POINT;
273 }
274 }
275
convertAddressingMode(int coordinateIndex,const vk::Sampler * sampler,VkImageViewType imageViewType)276 sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, const vk::Sampler *sampler, VkImageViewType imageViewType)
277 {
278 switch(imageViewType)
279 {
280 case VK_IMAGE_VIEW_TYPE_1D:
281 case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
282 if(coordinateIndex >= 1)
283 {
284 return ADDRESSING_UNUSED;
285 }
286 break;
287 case VK_IMAGE_VIEW_TYPE_2D:
288 case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
289 if(coordinateIndex == 2)
290 {
291 return ADDRESSING_UNUSED;
292 }
293 break;
294
295 case VK_IMAGE_VIEW_TYPE_3D:
296 break;
297
298 case VK_IMAGE_VIEW_TYPE_CUBE:
299 case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
300 if(coordinateIndex <= 1) // Cube faces themselves are addressed as 2D images.
301 {
302 // Vulkan 1.1 spec:
303 // "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
304 // 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
305 // is performed as described earlier in the Cube map edge handling section."
306 // This corresponds with our 'SEAMLESS' addressing mode.
307 return ADDRESSING_SEAMLESS;
308 }
309 else // coordinateIndex == 2
310 {
311 // The cube face is an index into 2D array layers.
312 return ADDRESSING_CUBEFACE;
313 }
314 break;
315
316 default:
317 UNSUPPORTED("imageViewType %d", imageViewType);
318 return ADDRESSING_WRAP;
319 }
320
321 if(!sampler)
322 {
323 // OpImageFetch does not take a sampler descriptor, but still needs a valid
324 // addressing mode that prevents out-of-bounds accesses:
325 // "The value returned by a read of an invalid texel is undefined, unless that
326 // read operation is from a buffer resource and the robustBufferAccess feature
327 // is enabled. In that case, an invalid texel is replaced as described by the
328 // robustBufferAccess feature." - Vulkan 1.1
329
330 // VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
331 // ADDRESSING_BORDER causes texel replacement to be performed.
332 // TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
333 return ADDRESSING_BORDER;
334 }
335
336 VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
337 switch(coordinateIndex)
338 {
339 case 0: addressMode = sampler->addressModeU; break;
340 case 1: addressMode = sampler->addressModeV; break;
341 case 2: addressMode = sampler->addressModeW; break;
342 default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
343 }
344
345 switch(addressMode)
346 {
347 case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
348 case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
349 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
350 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
351 case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
352 default:
353 UNSUPPORTED("addressMode %d", addressMode);
354 return ADDRESSING_WRAP;
355 }
356 }
357
358 } // namespace sw
359