• 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"
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->filteringPrecision == VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE);
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 
79 			samplerState.mipLodBias = vkSamplerState->mipLodBias;
80 			samplerState.maxAnisotropy = vkSamplerState->maxAnisotropy;
81 			samplerState.minLod = vkSamplerState->minLod;
82 			samplerState.maxLod = vkSamplerState->maxLod;
83 
84 			// If there's a single mip level and filtering doesn't depend on the LOD level,
85 			// the sampler will need to compute the LOD to produce the proper result.
86 			// Otherwise, it can be ignored.
87 			// We can skip the LOD computation for all modes, except LOD query,
88 			// where we have to return the proper value even if nothing else requires it.
89 			if(imageViewState.singleMipLevel &&
90 			   (samplerState.textureFilter != FILTER_MIN_POINT_MAG_LINEAR) &&
91 			   (samplerState.textureFilter != FILTER_MIN_LINEAR_MAG_POINT) &&
92 			   (samplerMethod != Query))
93 			{
94 				samplerState.minLod = 0.0f;
95 				samplerState.maxLod = 0.0f;
96 			}
97 		}
98 		else if(samplerMethod == Fetch)
99 		{
100 			// OpImageFetch does not take a sampler descriptor, but for VK_EXT_image_robustness
101 			// requires replacing invalid texels with zero.
102 			// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
103 			samplerState.border = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
104 
105 			// If there's a single mip level we can skip LOD computation.
106 			if(imageViewState.singleMipLevel)
107 			{
108 				samplerState.minLod = 0.0f;
109 				samplerState.maxLod = 0.0f;
110 			}
111 		}
112 		else if(samplerMethod == Write)
113 		{
114 			return emitWriteRoutine(instruction, samplerState);
115 		}
116 		else
117 			ASSERT(false);
118 
119 		return emitSamplerRoutine(instruction, samplerState);
120 	};
121 
122 	vk::Device::SamplingRoutineCache *cache = device->getSamplingRoutineCache();
123 	auto routine = cache->getOrCreate(key, createSamplingRoutine);
124 
125 	return (ImageSampler *)(routine->getEntry());
126 }
127 
emitWriteRoutine(ImageInstructionSignature instruction,const Sampler & samplerState)128 std::shared_ptr<rr::Routine> SpirvEmitter::emitWriteRoutine(ImageInstructionSignature instruction, const Sampler &samplerState)
129 {
130 	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
131 	rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
132 	{
133 		Pointer<Byte> descriptor = function.Arg<0>();
134 		Pointer<SIMD::Float> coord = function.Arg<1>();
135 		Pointer<SIMD::Float> texelAndMask = function.Arg<2>();
136 		Pointer<Byte> constants = function.Arg<3>();
137 
138 		WriteImage(instruction, descriptor, coord, texelAndMask, samplerState.textureFormat);
139 	}
140 
141 	return function("sampler");
142 }
143 
emitSamplerRoutine(ImageInstructionSignature instruction,const Sampler & samplerState)144 std::shared_ptr<rr::Routine> SpirvEmitter::emitSamplerRoutine(ImageInstructionSignature instruction, const Sampler &samplerState)
145 {
146 	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
147 	rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
148 	{
149 		Pointer<Byte> texture = function.Arg<0>();
150 		Pointer<SIMD::Float> in = function.Arg<1>();
151 		Pointer<SIMD::Float> out = function.Arg<2>();
152 		Pointer<Byte> constants = function.Arg<3>();
153 
154 		SIMD::Float uvwa[4];
155 		SIMD::Float dRef;
156 		SIMD::Float lodOrBias;  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
157 		SIMD::Float dsx[4];
158 		SIMD::Float dsy[4];
159 		SIMD::Int offset[4];
160 		SIMD::Int sampleId;
161 		SamplerFunction samplerFunction = instruction.getSamplerFunction();
162 
163 		uint32_t i = 0;
164 		for(; i < instruction.coordinates; i++)
165 		{
166 			uvwa[i] = in[i];
167 		}
168 
169 		if(instruction.isDref())
170 		{
171 			dRef = in[i];
172 			i++;
173 		}
174 
175 		if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
176 		{
177 			lodOrBias = in[i];
178 			i++;
179 		}
180 		else if(instruction.samplerMethod == Grad)
181 		{
182 			for(uint32_t j = 0; j < instruction.grad; j++, i++)
183 			{
184 				dsx[j] = in[i];
185 			}
186 
187 			for(uint32_t j = 0; j < instruction.grad; j++, i++)
188 			{
189 				dsy[j] = in[i];
190 			}
191 		}
192 
193 		for(uint32_t j = 0; j < instruction.offset; j++, i++)
194 		{
195 			offset[j] = As<SIMD::Int>(in[i]);
196 		}
197 
198 		if(instruction.sample)
199 		{
200 			sampleId = As<SIMD::Int>(in[i]);
201 		}
202 
203 		SamplerCore s(constants, samplerState, samplerFunction);
204 
205 		// For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
206 		// a single LOD per four elements, so we sample the image again for each LOD separately.
207 		// TODO(b/133868964) Pass down 4 component lodOrBias, dsx, and dsy to sampleTexture
208 		if(samplerFunction.method == Lod || samplerFunction.method == Grad ||
209 		   samplerFunction.method == Bias || samplerFunction.method == Fetch)
210 		{
211 			// Only perform per-lane sampling if LOD diverges or we're doing Grad sampling.
212 			Bool perLaneSampling = (samplerFunction.method == Grad) || Divergent(As<SIMD::Int>(lodOrBias));
213 			auto lod = Pointer<Float>(&lodOrBias);
214 			Int i = 0;
215 			Do
216 			{
217 				SIMD::Float dPdx;
218 				SIMD::Float dPdy;
219 				dPdx.x = Pointer<Float>(&dsx[0])[i];
220 				dPdx.y = Pointer<Float>(&dsx[1])[i];
221 				dPdx.z = Pointer<Float>(&dsx[2])[i];
222 
223 				dPdy.x = Pointer<Float>(&dsy[0])[i];
224 				dPdy.y = Pointer<Float>(&dsy[1])[i];
225 				dPdy.z = Pointer<Float>(&dsy[2])[i];
226 
227 				SIMD::Float4 sample = s.sampleTexture(texture, uvwa, dRef, lod[i], dPdx, dPdy, offset, sampleId);
228 
229 				If(perLaneSampling)
230 				{
231 					Pointer<Float> rgba = out;
232 					rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
233 					rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
234 					rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
235 					rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
236 					i++;
237 				}
238 				Else
239 				{
240 					Pointer<SIMD::Float> rgba = out;
241 					rgba[0] = sample.x;
242 					rgba[1] = sample.y;
243 					rgba[2] = sample.z;
244 					rgba[3] = sample.w;
245 					i = SIMD::Width;
246 				}
247 			}
248 			Until(i == SIMD::Width);
249 		}
250 		else
251 		{
252 			Float lod = Float(lodOrBias.x);
253 			SIMD::Float4 sample = s.sampleTexture(texture, uvwa, dRef, lod, (dsx[0]), (dsy[0]), offset, sampleId);
254 
255 			Pointer<SIMD::Float> rgba = out;
256 			rgba[0] = sample.x;
257 			rgba[1] = sample.y;
258 			rgba[2] = sample.z;
259 			rgba[3] = sample.w;
260 		}
261 	}
262 
263 	return function("sampler");
264 }
265 
convertFilterMode(const vk::SamplerState * samplerState,VkImageViewType imageViewType,SamplerMethod samplerMethod)266 sw::FilterType SpirvEmitter::convertFilterMode(const vk::SamplerState *samplerState, VkImageViewType imageViewType, SamplerMethod samplerMethod)
267 {
268 	if(samplerMethod == Gather)
269 	{
270 		return FILTER_GATHER;
271 	}
272 
273 	if(samplerMethod == Fetch)
274 	{
275 		return FILTER_POINT;
276 	}
277 
278 	if(samplerState->anisotropyEnable != VK_FALSE)
279 	{
280 		if(imageViewType == VK_IMAGE_VIEW_TYPE_2D || imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
281 		{
282 			if(samplerMethod != Lod)  // TODO(b/162926129): Support anisotropic filtering with explicit LOD.
283 			{
284 				return FILTER_ANISOTROPIC;
285 			}
286 		}
287 	}
288 
289 	switch(samplerState->magFilter)
290 	{
291 	case VK_FILTER_NEAREST:
292 		switch(samplerState->minFilter)
293 		{
294 		case VK_FILTER_NEAREST: return FILTER_POINT;
295 		case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
296 		default:
297 			UNSUPPORTED("minFilter %d", samplerState->minFilter);
298 			return FILTER_POINT;
299 		}
300 		break;
301 	case VK_FILTER_LINEAR:
302 		switch(samplerState->minFilter)
303 		{
304 		case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
305 		case VK_FILTER_LINEAR: return FILTER_LINEAR;
306 		default:
307 			UNSUPPORTED("minFilter %d", samplerState->minFilter);
308 			return FILTER_POINT;
309 		}
310 		break;
311 	default:
312 		break;
313 	}
314 
315 	UNSUPPORTED("magFilter %d", samplerState->magFilter);
316 	return FILTER_POINT;
317 }
318 
convertMipmapMode(const vk::SamplerState * samplerState)319 sw::MipmapType SpirvEmitter::convertMipmapMode(const vk::SamplerState *samplerState)
320 {
321 	if(!samplerState)
322 	{
323 		return MIPMAP_POINT;  // Samplerless operations (OpImageFetch) can take an integer Lod operand.
324 	}
325 
326 	if(samplerState->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
327 	{
328 		// TODO(b/151263485): Check image view level count instead.
329 		return MIPMAP_NONE;
330 	}
331 
332 	switch(samplerState->mipmapMode)
333 	{
334 	case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
335 	case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
336 	default:
337 		UNSUPPORTED("mipmapMode %d", samplerState->mipmapMode);
338 		return MIPMAP_POINT;
339 	}
340 }
341 
convertAddressingMode(int coordinateIndex,const vk::SamplerState * samplerState,VkImageViewType imageViewType)342 sw::AddressingMode SpirvEmitter::convertAddressingMode(int coordinateIndex, const vk::SamplerState *samplerState, VkImageViewType imageViewType)
343 {
344 	switch(imageViewType)
345 	{
346 	case VK_IMAGE_VIEW_TYPE_1D:
347 	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
348 		if(coordinateIndex >= 1)
349 		{
350 			return ADDRESSING_UNUSED;
351 		}
352 		break;
353 	case VK_IMAGE_VIEW_TYPE_2D:
354 	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
355 		if(coordinateIndex == 2)
356 		{
357 			return ADDRESSING_UNUSED;
358 		}
359 		break;
360 
361 	case VK_IMAGE_VIEW_TYPE_3D:
362 		break;
363 
364 	case VK_IMAGE_VIEW_TYPE_CUBE:
365 	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
366 		if(coordinateIndex <= 1)  // Cube faces themselves are addressed as 2D images.
367 		{
368 			// Vulkan 1.1 spec:
369 			// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
370 			//  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
371 			//  is performed as described earlier in the Cube map edge handling section."
372 			// This corresponds with our 'SEAMLESS' addressing mode.
373 			return ADDRESSING_SEAMLESS;
374 		}
375 		else  // coordinateIndex == 2
376 		{
377 			// The cube face is an index into 2D array layers.
378 			return ADDRESSING_CUBEFACE;
379 		}
380 		break;
381 
382 	default:
383 		UNSUPPORTED("imageViewType %d", imageViewType);
384 		return ADDRESSING_WRAP;
385 	}
386 
387 	if(!samplerState)
388 	{
389 		// OpImageFetch does not take a sampler descriptor, but still needs a valid
390 		// addressing mode that prevents out-of-bounds accesses:
391 		// "The value returned by a read of an invalid texel is undefined, unless that
392 		//  read operation is from a buffer resource and the robustBufferAccess feature
393 		//  is enabled. In that case, an invalid texel is replaced as described by the
394 		//  robustBufferAccess feature." - Vulkan 1.1
395 
396 		// VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
397 		// ADDRESSING_BORDER causes texel replacement to be performed.
398 		// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
399 		return ADDRESSING_BORDER;
400 	}
401 
402 	VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
403 	switch(coordinateIndex)
404 	{
405 	case 0: addressMode = samplerState->addressModeU; break;
406 	case 1: addressMode = samplerState->addressModeV; break;
407 	case 2: addressMode = samplerState->addressModeW; break;
408 	default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
409 	}
410 
411 	switch(addressMode)
412 	{
413 	case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
414 	case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
415 	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
416 	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
417 	case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
418 	default:
419 		UNSUPPORTED("addressMode %d", addressMode);
420 		return ADDRESSING_WRAP;
421 	}
422 }
423 
424 }  // namespace sw
425