1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 #include "SpirvShader.hpp"
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"
26 #include <spirv/unified1/spirv.hpp>
33 SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, vk::SampledImageDescriptor const *imageDescriptor, const vk::Sampler *sampler)
35 ImageInstruction instruction(inst);
36 const auto samplerId = sampler ? sampler->id : 0;
37 ASSERT(imageDescriptor->imageViewId != 0 && (samplerId != 0 || instruction.samplerMethod == Fetch));
39 vk::Device::SamplingRoutineCache::Key key = { inst, imageDescriptor->imageViewId, samplerId };
41 ASSERT(imageDescriptor->device);
43 if(auto routine = imageDescriptor->device->querySnapshotCache(key))
45 return (ImageSampler *)(routine->getEntry());
48 std::unique_lock<std::mutex> lock(imageDescriptor->device->getSamplingRoutineCacheMutex());
49 vk::Device::SamplingRoutineCache *cache = imageDescriptor->device->getSamplingRoutineCache();
51 auto routine = cache->query(key);
54 return (ImageSampler *)(routine->getEntry());
57 auto type = imageDescriptor->type;
59 Sampler samplerState = {};
60 samplerState.textureType = type;
61 samplerState.textureFormat = imageDescriptor->format;
63 samplerState.addressingModeU = convertAddressingMode(0, sampler, type);
64 samplerState.addressingModeV = convertAddressingMode(1, sampler, type);
65 samplerState.addressingModeW = convertAddressingMode(2, sampler, type);
66 samplerState.addressingModeY = convertAddressingMode(3, sampler, type);
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);
78 samplerState.textureFilter = (instruction.samplerMethod == Gather) ? FILTER_GATHER : convertFilterMode(sampler);
79 samplerState.border = sampler->borderColor;
81 samplerState.mipmapFilter = convertMipmapMode(sampler);
83 samplerState.compareEnable = (sampler->compareEnable != VK_FALSE);
84 samplerState.compareOp = sampler->compareOp;
85 samplerState.unnormalizedCoordinates = (sampler->unnormalizedCoordinates != VK_FALSE);
87 samplerState.ycbcrModel = sampler->ycbcrModel;
88 samplerState.studioSwing = sampler->studioSwing;
89 samplerState.swappedChroma = sampler->swappedChroma;
91 samplerState.mipLodBias = sampler->mipLodBias;
92 samplerState.maxAnisotropy = sampler->maxAnisotropy;
93 samplerState.minLod = sampler->minLod;
94 samplerState.maxLod = sampler->maxLod;
97 routine = emitSamplerRoutine(instruction, samplerState);
99 cache->add(key, routine);
100 return (ImageSampler *)(routine->getEntry());
103 std::shared_ptr<rr::Routine> SpirvShader::emitSamplerRoutine(ImageInstruction instruction, const Sampler &samplerState)
105 // TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
106 rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
108 Pointer<Byte> texture = function.Arg<0>();
109 Pointer<SIMD::Float> in = function.Arg<1>();
110 Pointer<SIMD::Float> out = function.Arg<2>();
111 Pointer<Byte> constants = function.Arg<3>();
113 SIMD::Float uvw[4] = { 0, 0, 0, 0 };
115 SIMD::Float lodOrBias = 0; // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
116 Vector4f dsx = { 0, 0, 0, 0 };
117 Vector4f dsy = { 0, 0, 0, 0 };
118 Vector4f offset = { 0, 0, 0, 0 };
119 SIMD::Int sampleId = 0;
120 SamplerFunction samplerFunction = instruction.getSamplerFunction();
123 for(; i < instruction.coordinates; i++)
128 if(instruction.isDref())
134 // TODO(b/134669567): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
135 // Implement optimized 1D sampling.
136 if(samplerState.textureType == VK_IMAGE_VIEW_TYPE_1D)
138 uvw[1] = SIMD::Float(0);
140 else if(samplerState.textureType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)
142 uvw[1] = SIMD::Float(0);
143 uvw[2] = in[1]; // Move 1D layer coordinate to 2D layer coordinate index.
146 if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
151 else if(instruction.samplerMethod == Grad)
153 for(uint32_t j = 0; j < instruction.grad; j++, i++)
158 for(uint32_t j = 0; j < instruction.grad; j++, i++)
164 for(uint32_t j = 0; j < instruction.offset; j++, i++)
169 if(instruction.sample)
171 sampleId = As<SIMD::Int>(in[i]);
174 SamplerCore s(constants, samplerState);
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.
180 auto lod = Pointer<Float>(&lodOrBias);
182 For(Int i = 0, i < SIMD::Width, i++)
187 dPdx.x = Pointer<Float>(&dsx.x)[i];
188 dPdx.y = Pointer<Float>(&dsx.y)[i];
189 dPdx.z = Pointer<Float>(&dsx.z)[i];
191 dPdy.x = Pointer<Float>(&dsy.x)[i];
192 dPdy.y = Pointer<Float>(&dsy.y)[i];
193 dPdy.z = Pointer<Float>(&dsy.z)[i];
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)
198 dPdx.y = Float(0.0f);
199 dPdy.y = Float(0.0f);
202 Vector4f sample = s.sampleTexture(texture, uvw, q, lod[i], dPdx, dPdy, offset, sampleId, samplerFunction);
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];
213 Vector4f sample = s.sampleTexture(texture, uvw, q, lodOrBias.x, (dsx.x), (dsy.x), offset, sampleId, samplerFunction);
215 Pointer<SIMD::Float> rgba = out;
223 return function("sampler");
226 sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler)
228 if(sampler->anisotropyEnable != VK_FALSE)
230 return FILTER_ANISOTROPIC;
233 switch(sampler->magFilter)
235 case VK_FILTER_NEAREST:
236 switch(sampler->minFilter)
238 case VK_FILTER_NEAREST: return FILTER_POINT;
239 case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
241 UNSUPPORTED("minFilter %d", sampler->minFilter);
245 case VK_FILTER_LINEAR:
246 switch(sampler->minFilter)
248 case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
249 case VK_FILTER_LINEAR: return FILTER_LINEAR;
251 UNSUPPORTED("minFilter %d", sampler->minFilter);
259 UNSUPPORTED("magFilter %d", sampler->magFilter);
263 sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
267 return MIPMAP_POINT; // Samplerless operations (OpImageFetch) can take an integer Lod operand.
270 if(sampler->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
272 // TODO(b/151263485): Check image view level count instead.
276 switch(sampler->mipmapMode)
278 case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
279 case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
281 UNSUPPORTED("mipmapMode %d", sampler->mipmapMode);
286 sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, const vk::Sampler *sampler, VkImageViewType imageViewType)
288 switch(imageViewType)
290 case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
291 if(coordinateIndex == 3)
293 return ADDRESSING_LAYER;
295 // Fall through to CUBE case:
296 case VK_IMAGE_VIEW_TYPE_CUBE:
297 if(coordinateIndex <= 1) // Cube faces themselves are addressed as 2D images.
300 // "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
301 // 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
302 // is performed as described earlier in the Cube map edge handling section."
303 // This corresponds with our 'SEAMLESS' addressing mode.
304 return ADDRESSING_SEAMLESS;
306 else if(coordinateIndex == 2)
308 // The cube face is an index into array layers.
309 return ADDRESSING_CUBEFACE;
313 return ADDRESSING_UNUSED;
317 case VK_IMAGE_VIEW_TYPE_1D: // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
318 if(coordinateIndex == 1)
320 return ADDRESSING_WRAP;
322 else if(coordinateIndex >= 2)
324 return ADDRESSING_UNUSED;
328 case VK_IMAGE_VIEW_TYPE_3D:
329 if(coordinateIndex >= 3)
331 return ADDRESSING_UNUSED;
335 case VK_IMAGE_VIEW_TYPE_1D_ARRAY: // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
336 if(coordinateIndex == 1)
338 return ADDRESSING_WRAP;
340 // Fall through to 2D_ARRAY case:
341 case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
342 if(coordinateIndex == 2)
344 return ADDRESSING_LAYER;
346 else if(coordinateIndex >= 3)
348 return ADDRESSING_UNUSED;
350 // Fall through to 2D case:
351 case VK_IMAGE_VIEW_TYPE_2D:
352 if(coordinateIndex >= 2)
354 return ADDRESSING_UNUSED;
359 UNSUPPORTED("imageViewType %d", imageViewType);
360 return ADDRESSING_WRAP;
365 // OpImageFetch does not take a sampler descriptor, but still needs a valid,
366 // arbitrary addressing mode that prevents out-of-bounds accesses:
367 // "The value returned by a read of an invalid texel is undefined, unless that
368 // read operation is from a buffer resource and the robustBufferAccess feature
369 // is enabled. In that case, an invalid texel is replaced as described by the
370 // robustBufferAccess feature." - Vulkan 1.1
372 return ADDRESSING_WRAP;
375 VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
376 switch(coordinateIndex)
378 case 0: addressMode = sampler->addressModeU; break;
379 case 1: addressMode = sampler->addressModeV; break;
380 case 2: addressMode = sampler->addressModeW; break;
381 default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
386 case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
387 case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
388 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
389 case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
390 case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
392 UNSUPPORTED("addressMode %d", addressMode);
393 return ADDRESSING_WRAP;