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