• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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