<commit_before>// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SpirvShader.hpp"

#include "SamplerCore.hpp" // TODO: Figure out what's needed.
#include "System/Math.hpp"
#include "Vulkan/VkBuffer.hpp"
#include "Vulkan/VkDebug.hpp"
#include "Vulkan/VkDescriptorSet.hpp"
#include "Vulkan/VkPipelineLayout.hpp"
#include "Vulkan/VkImageView.hpp"
#include "Vulkan/VkSampler.hpp"
#include "Vulkan/VkDescriptorSetLayout.hpp"
#include "Device/Config.hpp"

#include <spirv/unified1/spirv.hpp>
#include <spirv/unified1/GLSL.std.450.h>

#include <mutex>

#ifdef Bool
#undef Bool // b/127920555
#undef None
#endif

namespace sw {

SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, const vk::ImageView *imageView, const vk::Sampler *sampler)
{
	ImageInstruction instruction(inst);
	ASSERT(imageView->id != 0 && (sampler->id != 0 || instruction.samplerMethod == Fetch));

	// TODO(b/129523279): Move somewhere sensible.
	static std::unordered_map<uint64_t, ImageSampler*> cache;
	static std::mutex mutex;

	// FIXME(b/129523279): Take instruction opcode and optional parameters into acount (SamplerMethod / SamplerOption).
	auto key = (static_cast<uint64_t>(imageView->id) << 32) | static_cast<uint64_t>(sampler->id);

	std::unique_lock<std::mutex> lock(mutex);
	auto it = cache.find(key);
	if (it != cache.end()) { return it->second; }

	Sampler samplerState = {};
	samplerState.textureType = convertTextureType(imageView->getType());
	samplerState.textureFormat = imageView->getFormat();
	samplerState.textureFilter = convertFilterMode(sampler);
	samplerState.border = sampler->borderColor;

	samplerState.addressingModeU = convertAddressingMode(0, sampler->addressModeU, imageView->getType());
	samplerState.addressingModeV = convertAddressingMode(1, sampler->addressModeV, imageView->getType());
	samplerState.addressingModeW = convertAddressingMode(2, sampler->addressModeW, imageView->getType());

	samplerState.mipmapFilter = convertMipmapMode(sampler);
	samplerState.swizzle = imageView->getComponentMapping();
	samplerState.highPrecisionFiltering = false;
	samplerState.compare = COMPARE_BYPASS;                  ASSERT(sampler->compareEnable == VK_FALSE);  // TODO(b/129523279)

	ASSERT(sampler->anisotropyEnable == VK_FALSE);  // TODO(b/129523279)
	ASSERT(sampler->unnormalizedCoordinates == VK_FALSE);  // TODO(b/129523279)

	auto fptr = emitSamplerFunction(instruction, samplerState);

	cache.emplace(key, fptr);
	return fptr;
}

SpirvShader::ImageSampler *SpirvShader::emitSamplerFunction(ImageInstruction instruction, const Sampler &samplerState)
{
	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
	Function<Void(Pointer<Byte>, Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
	{
		Pointer<Byte> texture = function.Arg<0>();
		Pointer<Byte> sampler = function.Arg<1>();
		Pointer<SIMD::Float> in = function.Arg<2>();
		Pointer<SIMD::Float> out = function.Arg<3>();
		Pointer<Byte> constants = function.Arg<4>();

		SamplerCore s(constants, samplerState);

		SIMD::Float uvw[3];
		SIMD::Float q(0);     // TODO(b/129523279)
		SIMD::Float lodOrBias(0);  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
		Vector4f dsx;
		Vector4f dsy;
		Vector4f offset;
		SamplerFunction samplerFunction = instruction.getSamplerFunction();

		uint32_t i = 0;
		for( ; i < instruction.coordinates; i++)
		{
			uvw[i] = in[i];
		}

		// TODO(b/129523279): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
		// Implement optimized 1D sampling.
		if(samplerState.textureType == TEXTURE_1D ||
			samplerState.textureType == TEXTURE_1D_ARRAY)
		{
			uvw[1] = SIMD::Float(0);
		}

		if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias)
		{
			lodOrBias = in[i];
			i++;
		}
		else if(instruction.samplerMethod == Grad)
		{
			for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
			{
				dsx[j] = in[i];
			}

			for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
			{
				dsy[j] = in[i];
			}
		}

		if(instruction.samplerOption == Offset)
		{
			for(uint32_t j = 0; j < instruction.offsetComponents; j++, i++)
			{
				offset[j] = in[i];
			}
		}

		Vector4f sample = s.sampleTexture(texture, sampler, uvw[0], uvw[1], uvw[2], q, lodOrBias, dsx, dsy, offset, samplerFunction);

		Pointer<SIMD::Float> rgba = out;
		rgba[0] = sample.x;
		rgba[1] = sample.y;
		rgba[2] = sample.z;
		rgba[3] = sample.w;
	}

	return (ImageSampler*)function("sampler")->getEntry();
}

sw::TextureType SpirvShader::convertTextureType(VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_1D:         return TEXTURE_1D;
	case VK_IMAGE_VIEW_TYPE_2D:         return TEXTURE_2D;
//	case VK_IMAGE_VIEW_TYPE_3D:         return TEXTURE_3D;
	case VK_IMAGE_VIEW_TYPE_CUBE:       return TEXTURE_CUBE;
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return TEXTURE_1D_ARRAY;
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return TEXTURE_2D_ARRAY;
//	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return TEXTURE_CUBE_ARRAY;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return TEXTURE_2D;
	}
}

sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler)
{
	switch(sampler->magFilter)
	{
	case VK_FILTER_NEAREST:
		switch(sampler->minFilter)
		{
		case VK_FILTER_NEAREST: return FILTER_POINT;
		case VK_FILTER_LINEAR:  return FILTER_MIN_LINEAR_MAG_POINT;
		default:
			UNIMPLEMENTED("minFilter %d", sampler->minFilter);
			return FILTER_POINT;
		}
		break;
	case VK_FILTER_LINEAR:
		switch(sampler->minFilter)
		{
		case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
		case VK_FILTER_LINEAR:  return FILTER_LINEAR;
		default:
			UNIMPLEMENTED("minFilter %d", sampler->minFilter);
			return FILTER_POINT;
		}
		break;
	default:
		UNIMPLEMENTED("magFilter %d", sampler->magFilter);
		return FILTER_POINT;
	}
}

sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
{
	switch(sampler->mipmapMode)
	{
	case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
	case VK_SAMPLER_MIPMAP_MODE_LINEAR:  return MIPMAP_LINEAR;
	default:
		UNIMPLEMENTED("mipmapMode %d", sampler->mipmapMode);
		return MIPMAP_POINT;
	}
}

sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, VkSamplerAddressMode addressMode, VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_CUBE:
		break;
	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
		UNSUPPORTED("SPIR-V ImageCubeArray Capability (imageViewType: %d)", int(imageViewType));
		if(coordinateIndex == 3)
		{
			return ADDRESSING_LAYER;
		}
		break;
	case VK_IMAGE_VIEW_TYPE_1D:
	case VK_IMAGE_VIEW_TYPE_2D:
//	case VK_IMAGE_VIEW_TYPE_3D:
		break;
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
		break;
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
		if(coordinateIndex == 2)
		{
			return ADDRESSING_LAYER;
		}
		break;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return ADDRESSING_WRAP;
	}

	// Vulkan 1.1 spec:
	// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
	//  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
	//  is performed as described earlier in the Cube map edge handling section."
	// This corresponds with our 'seamless' addressing mode.
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_CUBE:
		return ADDRESSING_SEAMLESS;
//	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
		UNSUPPORTED("SPIR-V ImageCubeArray Capability (imageViewType: %d)", int(imageViewType));
		return ADDRESSING_SEAMLESS;
	case VK_IMAGE_VIEW_TYPE_1D:
	case VK_IMAGE_VIEW_TYPE_2D:
//	case VK_IMAGE_VIEW_TYPE_3D:
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
		break;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return ADDRESSING_WRAP;
	}

	switch(addressMode)
	{
	case VK_SAMPLER_ADDRESS_MODE_REPEAT:               return ADDRESSING_WRAP;
	case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:      return ADDRESSING_MIRROR;
	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:        return ADDRESSING_CLAMP;
	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:      return ADDRESSING_BORDER;
	case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
	default:
		UNIMPLEMENTED("addressMode %d", addressMode);
		return ADDRESSING_WRAP;
	}
}

} // namespace sw
<commit_msg>Enable 3D texture sampling<commit_after>// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SpirvShader.hpp"

#include "SamplerCore.hpp" // TODO: Figure out what's needed.
#include "System/Math.hpp"
#include "Vulkan/VkBuffer.hpp"
#include "Vulkan/VkDebug.hpp"
#include "Vulkan/VkDescriptorSet.hpp"
#include "Vulkan/VkPipelineLayout.hpp"
#include "Vulkan/VkImageView.hpp"
#include "Vulkan/VkSampler.hpp"
#include "Vulkan/VkDescriptorSetLayout.hpp"
#include "Device/Config.hpp"

#include <spirv/unified1/spirv.hpp>
#include <spirv/unified1/GLSL.std.450.h>

#include <mutex>

#ifdef Bool
#undef Bool // b/127920555
#undef None
#endif

namespace sw {

SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, const vk::ImageView *imageView, const vk::Sampler *sampler)
{
	ImageInstruction instruction(inst);
	ASSERT(imageView->id != 0 && (sampler->id != 0 || instruction.samplerMethod == Fetch));

	// TODO(b/129523279): Move somewhere sensible.
	static std::unordered_map<uint64_t, ImageSampler*> cache;
	static std::mutex mutex;

	// FIXME(b/129523279): Take instruction opcode and optional parameters into acount (SamplerMethod / SamplerOption).
	auto key = (static_cast<uint64_t>(imageView->id) << 32) | static_cast<uint64_t>(sampler->id);

	std::unique_lock<std::mutex> lock(mutex);
	auto it = cache.find(key);
	if (it != cache.end()) { return it->second; }

	Sampler samplerState = {};
	samplerState.textureType = convertTextureType(imageView->getType());
	samplerState.textureFormat = imageView->getFormat();
	samplerState.textureFilter = convertFilterMode(sampler);
	samplerState.border = sampler->borderColor;

	samplerState.addressingModeU = convertAddressingMode(0, sampler->addressModeU, imageView->getType());
	samplerState.addressingModeV = convertAddressingMode(1, sampler->addressModeV, imageView->getType());
	samplerState.addressingModeW = convertAddressingMode(2, sampler->addressModeW, imageView->getType());

	samplerState.mipmapFilter = convertMipmapMode(sampler);
	samplerState.swizzle = imageView->getComponentMapping();
	samplerState.highPrecisionFiltering = false;
	samplerState.compare = COMPARE_BYPASS;                  ASSERT(sampler->compareEnable == VK_FALSE);  // TODO(b/129523279)

	ASSERT(sampler->anisotropyEnable == VK_FALSE);  // TODO(b/129523279)
	ASSERT(sampler->unnormalizedCoordinates == VK_FALSE);  // TODO(b/129523279)

	auto fptr = emitSamplerFunction(instruction, samplerState);

	cache.emplace(key, fptr);
	return fptr;
}

SpirvShader::ImageSampler *SpirvShader::emitSamplerFunction(ImageInstruction instruction, const Sampler &samplerState)
{
	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
	Function<Void(Pointer<Byte>, Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
	{
		Pointer<Byte> texture = function.Arg<0>();
		Pointer<Byte> sampler = function.Arg<1>();
		Pointer<SIMD::Float> in = function.Arg<2>();
		Pointer<SIMD::Float> out = function.Arg<3>();
		Pointer<Byte> constants = function.Arg<4>();

		SamplerCore s(constants, samplerState);

		SIMD::Float uvw[3];
		SIMD::Float q(0);     // TODO(b/129523279)
		SIMD::Float lodOrBias(0);  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
		Vector4f dsx;
		Vector4f dsy;
		Vector4f offset;
		SamplerFunction samplerFunction = instruction.getSamplerFunction();

		uint32_t i = 0;
		for( ; i < instruction.coordinates; i++)
		{
			uvw[i] = in[i];
		}

		// TODO(b/129523279): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
		// Implement optimized 1D sampling.
		if(samplerState.textureType == TEXTURE_1D ||
			samplerState.textureType == TEXTURE_1D_ARRAY)
		{
			uvw[1] = SIMD::Float(0);
		}

		if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias)
		{
			lodOrBias = in[i];
			i++;
		}
		else if(instruction.samplerMethod == Grad)
		{
			for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
			{
				dsx[j] = in[i];
			}

			for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
			{
				dsy[j] = in[i];
			}
		}

		if(instruction.samplerOption == Offset)
		{
			for(uint32_t j = 0; j < instruction.offsetComponents; j++, i++)
			{
				offset[j] = in[i];
			}
		}

		Vector4f sample = s.sampleTexture(texture, sampler, uvw[0], uvw[1], uvw[2], q, lodOrBias, dsx, dsy, offset, samplerFunction);

		Pointer<SIMD::Float> rgba = out;
		rgba[0] = sample.x;
		rgba[1] = sample.y;
		rgba[2] = sample.z;
		rgba[3] = sample.w;
	}

	return (ImageSampler*)function("sampler")->getEntry();
}

sw::TextureType SpirvShader::convertTextureType(VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_1D:         return TEXTURE_1D;
	case VK_IMAGE_VIEW_TYPE_2D:         return TEXTURE_2D;
	case VK_IMAGE_VIEW_TYPE_3D:         return TEXTURE_3D;
	case VK_IMAGE_VIEW_TYPE_CUBE:       return TEXTURE_CUBE;
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return TEXTURE_1D_ARRAY;
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return TEXTURE_2D_ARRAY;
//	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return TEXTURE_CUBE_ARRAY;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return TEXTURE_2D;
	}
}

sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler)
{
	switch(sampler->magFilter)
	{
	case VK_FILTER_NEAREST:
		switch(sampler->minFilter)
		{
		case VK_FILTER_NEAREST: return FILTER_POINT;
		case VK_FILTER_LINEAR:  return FILTER_MIN_LINEAR_MAG_POINT;
		default:
			UNIMPLEMENTED("minFilter %d", sampler->minFilter);
			return FILTER_POINT;
		}
		break;
	case VK_FILTER_LINEAR:
		switch(sampler->minFilter)
		{
		case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
		case VK_FILTER_LINEAR:  return FILTER_LINEAR;
		default:
			UNIMPLEMENTED("minFilter %d", sampler->minFilter);
			return FILTER_POINT;
		}
		break;
	default:
		UNIMPLEMENTED("magFilter %d", sampler->magFilter);
		return FILTER_POINT;
	}
}

sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
{
	switch(sampler->mipmapMode)
	{
	case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
	case VK_SAMPLER_MIPMAP_MODE_LINEAR:  return MIPMAP_LINEAR;
	default:
		UNIMPLEMENTED("mipmapMode %d", sampler->mipmapMode);
		return MIPMAP_POINT;
	}
}

sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, VkSamplerAddressMode addressMode, VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_CUBE:
		break;
	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
		UNSUPPORTED("SPIR-V ImageCubeArray Capability (imageViewType: %d)", int(imageViewType));
		if(coordinateIndex == 3)
		{
			return ADDRESSING_LAYER;
		}
		break;
	case VK_IMAGE_VIEW_TYPE_1D:
	case VK_IMAGE_VIEW_TYPE_2D:
	case VK_IMAGE_VIEW_TYPE_3D:
		break;
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
		break;
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
		if(coordinateIndex == 2)
		{
			return ADDRESSING_LAYER;
		}
		break;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return ADDRESSING_WRAP;
	}

	// Vulkan 1.1 spec:
	// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
	//  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
	//  is performed as described earlier in the Cube map edge handling section."
	// This corresponds with our 'seamless' addressing mode.
	switch(imageViewType)
	{
	case VK_IMAGE_VIEW_TYPE_CUBE:
		return ADDRESSING_SEAMLESS;
//	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
		UNSUPPORTED("SPIR-V ImageCubeArray Capability (imageViewType: %d)", int(imageViewType));
		return ADDRESSING_SEAMLESS;
	case VK_IMAGE_VIEW_TYPE_1D:
	case VK_IMAGE_VIEW_TYPE_2D:
	case VK_IMAGE_VIEW_TYPE_3D:
//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
		break;
	default:
		UNIMPLEMENTED("imageViewType %d", imageViewType);
		return ADDRESSING_WRAP;
	}

	switch(addressMode)
	{
	case VK_SAMPLER_ADDRESS_MODE_REPEAT:               return ADDRESSING_WRAP;
	case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:      return ADDRESSING_MIRROR;
	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:        return ADDRESSING_CLAMP;
	case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:      return ADDRESSING_BORDER;
	case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
	default:
		UNIMPLEMENTED("addressMode %d", addressMode);
		return ADDRESSING_WRAP;
	}
}

} // namespace sw
<|endoftext|>


<commit_before>// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SpirvShader.hpp"

#include "SamplerCore.hpp"  // TODO: Figure out what's needed.
#include "Device/Config.hpp"
#include "System/Debug.hpp"
#include "System/Math.hpp"
#include "Vulkan/VkDescriptorSetLayout.hpp"
#include "Vulkan/VkDevice.hpp"
#include "Vulkan/VkImageView.hpp"
#include "Vulkan/VkSampler.hpp"

#include <spirv/unified1/spirv.hpp>

#include <climits>
#include <mutex>

namespace sw {

SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, vk::SampledImageDescriptor const *imageDescriptor, const vk::Sampler *sampler)
{
	ImageInstruction instruction(inst);
	const auto samplerId = sampler ? sampler->id : 0;
	ASSERT(imageDescriptor->imageViewId != 0 && (samplerId != 0 || instruction.samplerMethod == Fetch));
	ASSERT(imageDescriptor->device);

	vk::Device::SamplingRoutineCache::Key key = { inst, imageDescriptor->imageViewId, samplerId };

	vk::Device::SamplingRoutineCache *cache = imageDescriptor->device->getSamplingRoutineCache();

	auto createSamplingRoutine = [&](const vk::Device::SamplingRoutineCache::Key &key) {
		auto type = imageDescriptor->type;

		Sampler samplerState = {};
		samplerState.textureType = type;
		samplerState.textureFormat = imageDescriptor->format;

		samplerState.addressingModeU = convertAddressingMode(0, sampler, type);
		samplerState.addressingModeV = convertAddressingMode(1, sampler, type);
		samplerState.addressingModeW = convertAddressingMode(2, sampler, type);

		samplerState.mipmapFilter = convertMipmapMode(sampler);
		samplerState.swizzle = imageDescriptor->swizzle;
		samplerState.gatherComponent = instruction.gatherComponent;

		if(sampler)
		{
			samplerState.textureFilter = convertFilterMode(sampler, type, instruction);
			samplerState.border = sampler->borderColor;

			samplerState.mipmapFilter = convertMipmapMode(sampler);
			samplerState.highPrecisionFiltering = (sampler->filteringPrecision == VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE);

			samplerState.compareEnable = (sampler->compareEnable != VK_FALSE);
			samplerState.compareOp = sampler->compareOp;
			samplerState.unnormalizedCoordinates = (sampler->unnormalizedCoordinates != VK_FALSE);

			samplerState.ycbcrModel = sampler->ycbcrModel;
			samplerState.studioSwing = sampler->studioSwing;
			samplerState.swappedChroma = sampler->swappedChroma;

			samplerState.mipLodBias = sampler->mipLodBias;
			samplerState.maxAnisotropy = sampler->maxAnisotropy;
			samplerState.minLod = sampler->minLod;
			samplerState.maxLod = sampler->maxLod;
		}
		else
		{
			// OpImageFetch does not take a sampler descriptor, but for VK_EXT_image_robustness
			// requires replacing invalid texels with zero.
			// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
			samplerState.border = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
		}

		return emitSamplerRoutine(instruction, samplerState);
	};

	auto routine = cache->getOrCreate(key, createSamplingRoutine);

	return (ImageSampler *)(routine->getEntry());
}

std::shared_ptr<rr::Routine> SpirvShader::emitSamplerRoutine(ImageInstruction instruction, const Sampler &samplerState)
{
	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
	rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
	{
		Pointer<Byte> texture = function.Arg<0>();
		Pointer<SIMD::Float> in = function.Arg<1>();
		Pointer<SIMD::Float> out = function.Arg<2>();
		Pointer<Byte> constants = function.Arg<3>();

		SIMD::Float uvwa[4] = { 0, 0, 0, 0 };
		SIMD::Float dRef = 0;
		SIMD::Float lodOrBias = 0;  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
		Vector4f dsx = { 0, 0, 0, 0 };
		Vector4f dsy = { 0, 0, 0, 0 };
		Vector4i offset = { 0, 0, 0, 0 };
		SIMD::Int sampleId = 0;
		SamplerFunction samplerFunction = instruction.getSamplerFunction();

		uint32_t i = 0;
		for(; i < instruction.coordinates; i++)
		{
			uvwa[i] = in[i];
		}

		if(instruction.isDref())
		{
			dRef = in[i];
			i++;
		}

		if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
		{
			lodOrBias = in[i];
			i++;
		}
		else if(instruction.samplerMethod == Grad)
		{
			for(uint32_t j = 0; j < instruction.grad; j++, i++)
			{
				dsx[j] = in[i];
			}

			for(uint32_t j = 0; j < instruction.grad; j++, i++)
			{
				dsy[j] = in[i];
			}
		}

		for(uint32_t j = 0; j < instruction.offset; j++, i++)
		{
			offset[j] = As<SIMD::Int>(in[i]);
		}

		if(instruction.sample)
		{
			sampleId = As<SIMD::Int>(in[i]);
		}

		SamplerCore s(constants, samplerState);

		// For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
		// a single LOD per four elements, so we sample the image again for each LOD separately.
		if(samplerFunction.method == Lod || samplerFunction.method == Grad)  // TODO(b/133868964): Also handle divergent Bias and Fetch with Lod.
		{
			auto lod = Pointer<Float>(&lodOrBias);

			For(Int i = 0, i < SIMD::Width, i++)
			{
				SIMD::Float dPdx;
				SIMD::Float dPdy;

				dPdx.x = Pointer<Float>(&dsx.x)[i];
				dPdx.y = Pointer<Float>(&dsx.y)[i];
				dPdx.z = Pointer<Float>(&dsx.z)[i];

				dPdy.x = Pointer<Float>(&dsy.x)[i];
				dPdy.y = Pointer<Float>(&dsy.y)[i];
				dPdy.z = Pointer<Float>(&dsy.z)[i];

				Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lod[i], dPdx, dPdy, offset, sampleId, samplerFunction);

				Pointer<Float> rgba = out;
				rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
				rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
				rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
				rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
			}
		}
		else
		{
			Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lodOrBias.x, (dsx.x), (dsy.x), offset, sampleId, samplerFunction);

			Pointer<SIMD::Float> rgba = out;
			rgba[0] = sample.x;
			rgba[1] = sample.y;
			rgba[2] = sample.z;
			rgba[3] = sample.w;
		}
	}

	return function("sampler");
}

sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler, VkImageViewType imageViewType, ImageInstruction instruction)
{
	if(instruction.samplerMethod == Gather)
	{
		return FILTER_GATHER;
	}

	if(instruction.samplerMethod == Fetch)
	{
		return FILTER_POINT;
	}

	if(sampler->anisotropyEnable != VK_FALSE)
	{
		if(imageViewType == VK_IMAGE_VIEW_TYPE_2D || imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
		{
			if(instruction.samplerMethod != Lod)  // TODO(b/162926129): Support anisotropic filtering with explicit LOD.
			{
				return FILTER_ANISOTROPIC;
			}
		}
	}

	switch(sampler->magFilter)
	{
		case VK_FILTER_NEAREST:
			switch(sampler->minFilter)
			{
				case VK_FILTER_NEAREST: return FILTER_POINT;
				case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
				default:
					UNSUPPORTED("minFilter %d", sampler->minFilter);
					return FILTER_POINT;
			}
			break;
		case VK_FILTER_LINEAR:
			switch(sampler->minFilter)
			{
				case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
				case VK_FILTER_LINEAR: return FILTER_LINEAR;
				default:
					UNSUPPORTED("minFilter %d", sampler->minFilter);
					return FILTER_POINT;
			}
			break;
		default:
			break;
	}

	UNSUPPORTED("magFilter %d", sampler->magFilter);
	return FILTER_POINT;
}

sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
{
	if(!sampler)
	{
		return MIPMAP_POINT;  // Samplerless operations (OpImageFetch) can take an integer Lod operand.
	}

	if(sampler->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
	{
		// TODO(b/151263485): Check image view level count instead.
		return MIPMAP_NONE;
	}

	switch(sampler->mipmapMode)
	{
		case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
		case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
		default:
			UNSUPPORTED("mipmapMode %d", sampler->mipmapMode);
			return MIPMAP_POINT;
	}
}

sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, const vk::Sampler *sampler, VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
		case VK_IMAGE_VIEW_TYPE_1D:
		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
			if(coordinateIndex >= 1)
			{
				return ADDRESSING_UNUSED;
			}
			break;
		case VK_IMAGE_VIEW_TYPE_2D:
		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
			if(coordinateIndex == 2)
			{
				return ADDRESSING_UNUSED;
			}
			break;

		case VK_IMAGE_VIEW_TYPE_3D:
			break;

		case VK_IMAGE_VIEW_TYPE_CUBE:
		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
			if(coordinateIndex <= 1)  // Cube faces themselves are addressed as 2D images.
			{
				// Vulkan 1.1 spec:
				// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
				//  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
				//  is performed as described earlier in the Cube map edge handling section."
				// This corresponds with our 'SEAMLESS' addressing mode.
				return ADDRESSING_SEAMLESS;
			}
			else  // coordinateIndex == 2
			{
				// The cube face is an index into 2D array layers.
				return ADDRESSING_CUBEFACE;
			}
			break;

		default:
			UNSUPPORTED("imageViewType %d", imageViewType);
			return ADDRESSING_WRAP;
	}

	if(!sampler)
	{
		// OpImageFetch does not take a sampler descriptor, but still needs a valid
		// addressing mode that prevents out-of-bounds accesses:
		// "The value returned by a read of an invalid texel is undefined, unless that
		//  read operation is from a buffer resource and the robustBufferAccess feature
		//  is enabled. In that case, an invalid texel is replaced as described by the
		//  robustBufferAccess feature." - Vulkan 1.1

		// VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
		// ADDRESSING_BORDER causes texel replacement to be performed.
		// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
		return ADDRESSING_BORDER;
	}

	VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	switch(coordinateIndex)
	{
		case 0: addressMode = sampler->addressModeU; break;
		case 1: addressMode = sampler->addressModeV; break;
		case 2: addressMode = sampler->addressModeW; break;
		default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
	}

	switch(addressMode)
	{
		case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
		case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
		case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
		default:
			UNSUPPORTED("addressMode %d", addressMode);
			return ADDRESSING_WRAP;
	}
}

}  // namespace sw
<commit_msg>Revert sampling parameter initialization workaround<commit_after>// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "SpirvShader.hpp"

#include "SamplerCore.hpp"  // TODO: Figure out what's needed.
#include "Device/Config.hpp"
#include "System/Debug.hpp"
#include "System/Math.hpp"
#include "Vulkan/VkDescriptorSetLayout.hpp"
#include "Vulkan/VkDevice.hpp"
#include "Vulkan/VkImageView.hpp"
#include "Vulkan/VkSampler.hpp"

#include <spirv/unified1/spirv.hpp>

#include <climits>
#include <mutex>

namespace sw {

SpirvShader::ImageSampler *SpirvShader::getImageSampler(uint32_t inst, vk::SampledImageDescriptor const *imageDescriptor, const vk::Sampler *sampler)
{
	ImageInstruction instruction(inst);
	const auto samplerId = sampler ? sampler->id : 0;
	ASSERT(imageDescriptor->imageViewId != 0 && (samplerId != 0 || instruction.samplerMethod == Fetch));
	ASSERT(imageDescriptor->device);

	vk::Device::SamplingRoutineCache::Key key = { inst, imageDescriptor->imageViewId, samplerId };

	vk::Device::SamplingRoutineCache *cache = imageDescriptor->device->getSamplingRoutineCache();

	auto createSamplingRoutine = [&](const vk::Device::SamplingRoutineCache::Key &key) {
		auto type = imageDescriptor->type;

		Sampler samplerState = {};
		samplerState.textureType = type;
		samplerState.textureFormat = imageDescriptor->format;

		samplerState.addressingModeU = convertAddressingMode(0, sampler, type);
		samplerState.addressingModeV = convertAddressingMode(1, sampler, type);
		samplerState.addressingModeW = convertAddressingMode(2, sampler, type);

		samplerState.mipmapFilter = convertMipmapMode(sampler);
		samplerState.swizzle = imageDescriptor->swizzle;
		samplerState.gatherComponent = instruction.gatherComponent;

		if(sampler)
		{
			samplerState.textureFilter = convertFilterMode(sampler, type, instruction);
			samplerState.border = sampler->borderColor;

			samplerState.mipmapFilter = convertMipmapMode(sampler);
			samplerState.highPrecisionFiltering = (sampler->filteringPrecision == VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE);

			samplerState.compareEnable = (sampler->compareEnable != VK_FALSE);
			samplerState.compareOp = sampler->compareOp;
			samplerState.unnormalizedCoordinates = (sampler->unnormalizedCoordinates != VK_FALSE);

			samplerState.ycbcrModel = sampler->ycbcrModel;
			samplerState.studioSwing = sampler->studioSwing;
			samplerState.swappedChroma = sampler->swappedChroma;

			samplerState.mipLodBias = sampler->mipLodBias;
			samplerState.maxAnisotropy = sampler->maxAnisotropy;
			samplerState.minLod = sampler->minLod;
			samplerState.maxLod = sampler->maxLod;
		}
		else
		{
			// OpImageFetch does not take a sampler descriptor, but for VK_EXT_image_robustness
			// requires replacing invalid texels with zero.
			// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
			samplerState.border = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
		}

		return emitSamplerRoutine(instruction, samplerState);
	};

	auto routine = cache->getOrCreate(key, createSamplingRoutine);

	return (ImageSampler *)(routine->getEntry());
}

std::shared_ptr<rr::Routine> SpirvShader::emitSamplerRoutine(ImageInstruction instruction, const Sampler &samplerState)
{
	// TODO(b/129523279): Hold a separate mutex lock for the sampler being built.
	rr::Function<Void(Pointer<Byte>, Pointer<SIMD::Float>, Pointer<SIMD::Float>, Pointer<Byte>)> function;
	{
		Pointer<Byte> texture = function.Arg<0>();
		Pointer<SIMD::Float> in = function.Arg<1>();
		Pointer<SIMD::Float> out = function.Arg<2>();
		Pointer<Byte> constants = function.Arg<3>();

		SIMD::Float uvwa[4];
		SIMD::Float dRef;
		SIMD::Float lodOrBias;  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
		Vector4f dsx;
		Vector4f dsy;
		Vector4i offset;
		SIMD::Int sampleId;
		SamplerFunction samplerFunction = instruction.getSamplerFunction();

		uint32_t i = 0;
		for(; i < instruction.coordinates; i++)
		{
			uvwa[i] = in[i];
		}

		if(instruction.isDref())
		{
			dRef = in[i];
			i++;
		}

		if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias || instruction.samplerMethod == Fetch)
		{
			lodOrBias = in[i];
			i++;
		}
		else if(instruction.samplerMethod == Grad)
		{
			for(uint32_t j = 0; j < instruction.grad; j++, i++)
			{
				dsx[j] = in[i];
			}

			for(uint32_t j = 0; j < instruction.grad; j++, i++)
			{
				dsy[j] = in[i];
			}
		}

		for(uint32_t j = 0; j < instruction.offset; j++, i++)
		{
			offset[j] = As<SIMD::Int>(in[i]);
		}

		if(instruction.sample)
		{
			sampleId = As<SIMD::Int>(in[i]);
		}

		SamplerCore s(constants, samplerState);

		// For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
		// a single LOD per four elements, so we sample the image again for each LOD separately.
		if(samplerFunction.method == Lod || samplerFunction.method == Grad)  // TODO(b/133868964): Also handle divergent Bias and Fetch with Lod.
		{
			auto lod = Pointer<Float>(&lodOrBias);

			For(Int i = 0, i < SIMD::Width, i++)
			{
				SIMD::Float dPdx;
				SIMD::Float dPdy;

				dPdx.x = Pointer<Float>(&dsx.x)[i];
				dPdx.y = Pointer<Float>(&dsx.y)[i];
				dPdx.z = Pointer<Float>(&dsx.z)[i];

				dPdy.x = Pointer<Float>(&dsy.x)[i];
				dPdy.y = Pointer<Float>(&dsy.y)[i];
				dPdy.z = Pointer<Float>(&dsy.z)[i];

				Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lod[i], dPdx, dPdy, offset, sampleId, samplerFunction);

				Pointer<Float> rgba = out;
				rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
				rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
				rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
				rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
			}
		}
		else
		{
			Vector4f sample = s.sampleTexture(texture, uvwa, dRef, lodOrBias.x, (dsx.x), (dsy.x), offset, sampleId, samplerFunction);

			Pointer<SIMD::Float> rgba = out;
			rgba[0] = sample.x;
			rgba[1] = sample.y;
			rgba[2] = sample.z;
			rgba[3] = sample.w;
		}
	}

	return function("sampler");
}

sw::FilterType SpirvShader::convertFilterMode(const vk::Sampler *sampler, VkImageViewType imageViewType, ImageInstruction instruction)
{
	if(instruction.samplerMethod == Gather)
	{
		return FILTER_GATHER;
	}

	if(instruction.samplerMethod == Fetch)
	{
		return FILTER_POINT;
	}

	if(sampler->anisotropyEnable != VK_FALSE)
	{
		if(imageViewType == VK_IMAGE_VIEW_TYPE_2D || imageViewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
		{
			if(instruction.samplerMethod != Lod)  // TODO(b/162926129): Support anisotropic filtering with explicit LOD.
			{
				return FILTER_ANISOTROPIC;
			}
		}
	}

	switch(sampler->magFilter)
	{
		case VK_FILTER_NEAREST:
			switch(sampler->minFilter)
			{
				case VK_FILTER_NEAREST: return FILTER_POINT;
				case VK_FILTER_LINEAR: return FILTER_MIN_LINEAR_MAG_POINT;
				default:
					UNSUPPORTED("minFilter %d", sampler->minFilter);
					return FILTER_POINT;
			}
			break;
		case VK_FILTER_LINEAR:
			switch(sampler->minFilter)
			{
				case VK_FILTER_NEAREST: return FILTER_MIN_POINT_MAG_LINEAR;
				case VK_FILTER_LINEAR: return FILTER_LINEAR;
				default:
					UNSUPPORTED("minFilter %d", sampler->minFilter);
					return FILTER_POINT;
			}
			break;
		default:
			break;
	}

	UNSUPPORTED("magFilter %d", sampler->magFilter);
	return FILTER_POINT;
}

sw::MipmapType SpirvShader::convertMipmapMode(const vk::Sampler *sampler)
{
	if(!sampler)
	{
		return MIPMAP_POINT;  // Samplerless operations (OpImageFetch) can take an integer Lod operand.
	}

	if(sampler->ycbcrModel != VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY)
	{
		// TODO(b/151263485): Check image view level count instead.
		return MIPMAP_NONE;
	}

	switch(sampler->mipmapMode)
	{
		case VK_SAMPLER_MIPMAP_MODE_NEAREST: return MIPMAP_POINT;
		case VK_SAMPLER_MIPMAP_MODE_LINEAR: return MIPMAP_LINEAR;
		default:
			UNSUPPORTED("mipmapMode %d", sampler->mipmapMode);
			return MIPMAP_POINT;
	}
}

sw::AddressingMode SpirvShader::convertAddressingMode(int coordinateIndex, const vk::Sampler *sampler, VkImageViewType imageViewType)
{
	switch(imageViewType)
	{
		case VK_IMAGE_VIEW_TYPE_1D:
		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
			if(coordinateIndex >= 1)
			{
				return ADDRESSING_UNUSED;
			}
			break;
		case VK_IMAGE_VIEW_TYPE_2D:
		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
			if(coordinateIndex == 2)
			{
				return ADDRESSING_UNUSED;
			}
			break;

		case VK_IMAGE_VIEW_TYPE_3D:
			break;

		case VK_IMAGE_VIEW_TYPE_CUBE:
		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
			if(coordinateIndex <= 1)  // Cube faces themselves are addressed as 2D images.
			{
				// Vulkan 1.1 spec:
				// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
				//  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
				//  is performed as described earlier in the Cube map edge handling section."
				// This corresponds with our 'SEAMLESS' addressing mode.
				return ADDRESSING_SEAMLESS;
			}
			else  // coordinateIndex == 2
			{
				// The cube face is an index into 2D array layers.
				return ADDRESSING_CUBEFACE;
			}
			break;

		default:
			UNSUPPORTED("imageViewType %d", imageViewType);
			return ADDRESSING_WRAP;
	}

	if(!sampler)
	{
		// OpImageFetch does not take a sampler descriptor, but still needs a valid
		// addressing mode that prevents out-of-bounds accesses:
		// "The value returned by a read of an invalid texel is undefined, unless that
		//  read operation is from a buffer resource and the robustBufferAccess feature
		//  is enabled. In that case, an invalid texel is replaced as described by the
		//  robustBufferAccess feature." - Vulkan 1.1

		// VK_EXT_image_robustness requires nullifying out-of-bounds accesses.
		// ADDRESSING_BORDER causes texel replacement to be performed.
		// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
		return ADDRESSING_BORDER;
	}

	VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
	switch(coordinateIndex)
	{
		case 0: addressMode = sampler->addressModeU; break;
		case 1: addressMode = sampler->addressModeV; break;
		case 2: addressMode = sampler->addressModeW; break;
		default: UNSUPPORTED("coordinateIndex: %d", coordinateIndex);
	}

	switch(addressMode)
	{
		case VK_SAMPLER_ADDRESS_MODE_REPEAT: return ADDRESSING_WRAP;
		case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: return ADDRESSING_MIRROR;
		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: return ADDRESSING_CLAMP;
		case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: return ADDRESSING_BORDER;
		case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: return ADDRESSING_MIRRORONCE;
		default:
			UNSUPPORTED("addressMode %d", addressMode);
			return ADDRESSING_WRAP;
	}
}

}  // namespace sw
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(XALANLOCATOR_HEADER_GUARD_1357924680)
#define XALANLOCATOR_HEADER_GUARD_1357924680

// Base include file.  Must be first.
#include <PlatformSupport/PlatformSupportDefinitions.hpp>

#include <xercesc/sax/Locator.hpp>

XALAN_CPP_NAMESPACE_BEGIN

/**
 * This class defines a base class for Locator derivations in Xalan.  It was defined
 * because Xerces made changes in their Locator class which caused turbulence.
 */
class XALAN_PLATFORMSUPPORT_EXPORT XalanLocator : public XERCES_CPP_NAMESPACE_QUALIFIER Locator
{
public:

	typedef XERCES_CPP_NAMESPACE_QUALIFIER Locator	ParentType;

#if XERCES_VERSION_MAJOR >= 2
	typedef XMLSSize_t	size_type;
#else
	typedef int			size_type;
#endif

	XalanLocator() {}

	virtual
	~XalanLocator() {}

	virtual const XMLCh*
	getPublicId() const = 0;

	virtual const XMLCh*
	getSystemId() const = 0;

	virtual size_type
	getLineNumber() const = 0;

	virtual size_type
	getColumnNumber() const = 0;

private:

	// Not defined...
	XalanLocator(const XalanLocator&);

	XalanLocator&
	operator=(const XalanLocator&);

};

XALAN_CPP_NAMESPACE_END

#endif	// PREFIXRESOLVER_HEADER_GUARD_1357924680
<commit_msg>Added some static functions to simplify using Locators.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#if !defined(XALANLOCATOR_HEADER_GUARD_1357924680)
#define XALANLOCATOR_HEADER_GUARD_1357924680

// Base include file.  Must be first.
#include <PlatformSupport/PlatformSupportDefinitions.hpp>

#include <xercesc/sax/Locator.hpp>

XALAN_CPP_NAMESPACE_BEGIN

/**
 * This class defines a base class for Locator derivations in Xalan.  It was defined
 * because Xerces made changes in their Locator class which caused turbulence.
 */
class XALAN_PLATFORMSUPPORT_EXPORT XalanLocator : public XERCES_CPP_NAMESPACE_QUALIFIER Locator
{
public:

	typedef XERCES_CPP_NAMESPACE_QUALIFIER Locator	ParentType;

#if XERCES_VERSION_MAJOR >= 2
	typedef XMLSSize_t	size_type;
#else
	typedef int			size_type;
#endif

	XalanLocator() {}

	virtual
	~XalanLocator() {}

	virtual const XMLCh*
	getPublicId() const = 0;

	virtual const XMLCh*
	getSystemId() const = 0;

	virtual size_type
	getLineNumber() const = 0;

	virtual size_type
	getColumnNumber() const = 0;

	static size_type
	getLineNumber(const ParentType*		theLocator)
	{
		return theLocator == 0 ? size_type(-1) : theLocator->getLineNumber();
	}

	static size_type
	getColumnNumber(const ParentType*	theLocator)
	{
		return theLocator == 0 ? size_type(-1) : theLocator->getColumnNumber();
	}

	static size_type
	getUnknownValue()
	{
		return size_type(-1);
	}

private:

	// Not defined...
	XalanLocator(const XalanLocator&);

	XalanLocator&
	operator=(const XalanLocator&);

};

XALAN_CPP_NAMESPACE_END

#endif	// PREFIXRESOLVER_HEADER_GUARD_1357924680
<|endoftext|>


<commit_before>/**
 *   SFCGAL
 *
 *   Copyright (C) 2012-2013 Oslandia <infos@oslandia.com>
 *   Copyright (C) 2012-2013 IGN (http://www.ign.fr)
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <SFCGAL/algorithm/orientation.h>

#include <SFCGAL/algorithm/ConsistentOrientationBuilder.h>

#include <SFCGAL/graph/GeometryGraph.h>
#include <SFCGAL/graph/GeometryGraphBuilder.h>
#include <SFCGAL/graph/algorithm/isHalfEdge.h>

#include <SFCGAL/graph/algorithm/orientation.h>

namespace SFCGAL {
namespace algorithm {

///
///
///
void makeValidOrientation( CGAL::Polygon_2< Kernel > & polygon )
{
	if ( polygon.orientation() != CGAL::COUNTERCLOCKWISE ){
		polygon.reverse_orientation() ;
	}
}

///
///
///
void makeValidOrientation( CGAL::Polygon_with_holes_2< Kernel > & polygon )
{
	typedef CGAL::Polygon_with_holes_2< Kernel > Polygon_with_holes_2 ;

	if ( polygon.outer_boundary().orientation() != CGAL::COUNTERCLOCKWISE ){
		polygon.outer_boundary().reverse_orientation() ;
	}
	for ( Polygon_with_holes_2::Hole_iterator it = polygon.holes_begin(); it != polygon.holes_end(); ++it ){
		if ( it->orientation() != CGAL::CLOCKWISE ){
			it->reverse_orientation() ;
		}
	}
}

///
///
///
void makeValidOrientation( Polygon & polygon )
{
	for ( size_t i = 0; i < polygon.numRings(); i++ ){
		LineString & ring = polygon.ringN(i);
		if ( i == 0 ){
			if ( ring.toPolygon_2().orientation() != CGAL::COUNTERCLOCKWISE ){
				ring.reverse() ;
			}
		}else{
			if ( ring.toPolygon_2().orientation() != CGAL::CLOCKWISE ){
				ring.reverse();
			}
		}
	}
}

///
///
///
bool hasConsistentOrientation3D( const TriangulatedSurface & g )
{
	using namespace graph ;

	if ( g.isEmpty() )
		return true ;

	GeometryGraph graph ;
	GeometryGraphBuilder graphBuilder( graph ) ;
	graphBuilder.addTriangulatedSurface( g );
	return graph::algorithm::isHalfEdge( graph ) ;
}

///
///
///
bool hasConsistentOrientation3D( const PolyhedralSurface & g )
{
	using namespace graph ;

	if ( g.isEmpty() )
		return true ;

	GeometryGraph graph ;
	GeometryGraphBuilder graphBuilder( graph ) ;
	graphBuilder.addPolyhedralSurface( g );
	return graph::algorithm::isHalfEdge( graph ) ;
}

///
///
///
void makeConsistentOrientation3D( TriangulatedSurface & g )
{
	ConsistentOrientationBuilder builder ;
	builder.addTriangulatedSurface(g);
	g = builder.buildTriangulatedSurface() ;
}

///
///
///
bool isCounterClockWiseOriented( const LineString& ls )
{
	// Compute the 'z' part of the Newell's formula
	// and test against 0
	Kernel::FT z = 0 ;
	for ( size_t i = 0; i < ls.numPoints() - 1; ++i )
	{
		const Point& pi = ls.pointN(i);
		const Point& pj = ls.pointN( (i+1) % ls.numPoints() );
		z += ( pi.x() - pj.x() ) * ( pi.y() + pj.y() );
	}
	return z > 0;
}

///
///
///
bool isCounterClockWiseOriented( const Triangle& tri )
{
	// Compute the 'z' part of the cross product

	return (tri.vertex(2).x() - tri.vertex(1).x()) * (tri.vertex(0).y() - tri.vertex(1).y()) -
		(tri.vertex(2).y() - tri.vertex(1).y()) * (tri.vertex(0).x() - tri.vertex(1).x()) > 0;
}

///
///
///
bool isCounterClockWiseOriented( const Polygon& poly )
{
	return isCounterClockWiseOriented( poly.exteriorRing() );
}

}//algorithm
}//SFCGAL

<commit_msg>[algorithm::orientation]simplify isCounterClockWiseOriented( const LineString& ls ) and fix problem with empty geometries<commit_after>/**
 *   SFCGAL
 *
 *   Copyright (C) 2012-2013 Oslandia <infos@oslandia.com>
 *   Copyright (C) 2012-2013 IGN (http://www.ign.fr)
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <SFCGAL/algorithm/orientation.h>

#include <SFCGAL/algorithm/ConsistentOrientationBuilder.h>

#include <SFCGAL/graph/GeometryGraph.h>
#include <SFCGAL/graph/GeometryGraphBuilder.h>
#include <SFCGAL/graph/algorithm/isHalfEdge.h>

#include <SFCGAL/graph/algorithm/orientation.h>

namespace SFCGAL {
namespace algorithm {

///
///
///
void makeValidOrientation( CGAL::Polygon_2< Kernel > & polygon )
{
	if ( polygon.orientation() != CGAL::COUNTERCLOCKWISE ){
		polygon.reverse_orientation() ;
	}
}

///
///
///
void makeValidOrientation( CGAL::Polygon_with_holes_2< Kernel > & polygon )
{
	typedef CGAL::Polygon_with_holes_2< Kernel > Polygon_with_holes_2 ;

	if ( polygon.outer_boundary().orientation() != CGAL::COUNTERCLOCKWISE ){
		polygon.outer_boundary().reverse_orientation() ;
	}
	for ( Polygon_with_holes_2::Hole_iterator it = polygon.holes_begin(); it != polygon.holes_end(); ++it ){
		if ( it->orientation() != CGAL::CLOCKWISE ){
			it->reverse_orientation() ;
		}
	}
}

///
///
///
void makeValidOrientation( Polygon & polygon )
{
	for ( size_t i = 0; i < polygon.numRings(); i++ ){
		LineString & ring = polygon.ringN(i);
		if ( i == 0 ){
			if ( ring.toPolygon_2().orientation() != CGAL::COUNTERCLOCKWISE ){
				ring.reverse() ;
			}
		}else{
			if ( ring.toPolygon_2().orientation() != CGAL::CLOCKWISE ){
				ring.reverse();
			}
		}
	}
}

///
///
///
bool hasConsistentOrientation3D( const TriangulatedSurface & g )
{
	using namespace graph ;

	if ( g.isEmpty() )
		return true ;

	GeometryGraph graph ;
	GeometryGraphBuilder graphBuilder( graph ) ;
	graphBuilder.addTriangulatedSurface( g );
	return graph::algorithm::isHalfEdge( graph ) ;
}

///
///
///
bool hasConsistentOrientation3D( const PolyhedralSurface & g )
{
	using namespace graph ;

	if ( g.isEmpty() )
		return true ;

	GeometryGraph graph ;
	GeometryGraphBuilder graphBuilder( graph ) ;
	graphBuilder.addPolyhedralSurface( g );
	return graph::algorithm::isHalfEdge( graph ) ;
}

///
///
///
void makeConsistentOrientation3D( TriangulatedSurface & g )
{
	ConsistentOrientationBuilder builder ;
	builder.addTriangulatedSurface(g);
	g = builder.buildTriangulatedSurface() ;
}

///
///
///
bool isCounterClockWiseOriented( const LineString& ls )
{
	// Compute the 'z' part of the Newell's formula
	// and test against 0
	Kernel::FT z = 0 ;
	for ( size_t i = 0; i < ls.numSegments(); ++i )
	{
		const Point& pi = ls.pointN(i);
		const Point& pj = ls.pointN(i+1);
		z += ( pi.x() - pj.x() ) * ( pi.y() + pj.y() );
	}
	return z > 0;
}

///
///
///
bool isCounterClockWiseOriented( const Triangle& tri )
{
	// Compute the 'z' part of the cross product

	return (tri.vertex(2).x() - tri.vertex(1).x()) * (tri.vertex(0).y() - tri.vertex(1).y()) -
		(tri.vertex(2).y() - tri.vertex(1).y()) * (tri.vertex(0).x() - tri.vertex(1).x()) > 0;
}

///
///
///
bool isCounterClockWiseOriented( const Polygon& poly )
{
	return isCounterClockWiseOriented( poly.exteriorRing() );
}

}//algorithm
}//SFCGAL

<|endoftext|>


<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"

#include "HeapBlockDevice.h"
#include <stdlib.h>

using namespace utest::v1;

#define TEST_BLOCK_SIZE 512
#define TEST_BLOCK_COUNT 10
#define TEST_BLOCK_DEVICE_SIZE 16*TEST_BLOCK_SIZE
#define TEST_ERROR_MASK 16

const struct {
    const char *name;
    bd_size_t (BlockDevice::*method)() const;
} ATTRS[] = {
    {"read size",    &BlockDevice::get_read_size},
    {"program size", &BlockDevice::get_program_size},
    {"erase size",   &BlockDevice::get_erase_size},
    {"total size",   &BlockDevice::size},
};

// Simple test that read/writes random set of blocks
void test_read_write() {
    HeapBlockDevice bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);

    int err = bd.init();
    TEST_ASSERT_EQUAL(0, err);

    for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) {
        for (int i = 3; i >= 0; i--) {
            bd_size_t size = (bd.*ATTRS[a].method)();
            if (size >= (1ULL << 10*i)) {
                break;
            }
        }
    }

    bd_size_t block_size = bd.get_erase_size();
    uint8_t *write_block = new uint8_t[block_size];
    uint8_t *read_block = new uint8_t[block_size];
    uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK];

    for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
        // Find a random block
        bd_addr_t block = (rand()*block_size) % bd.size();

        // Use next random number as temporary seed to keep
        // the address progressing in the pseudorandom sequence
        unsigned seed = rand();

        // Fill with random sequence
        srand(seed);
        for (bd_size_t i = 0; i < block_size; i++) {
            write_block[i] = 0xff & rand();
        }

        err = bd.erase(block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        err = bd.program(write_block, block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        err = bd.read(read_block, block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        // Find error mask for debugging
        memset(error_mask, 0, TEST_ERROR_MASK);
        bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8);

        srand(seed);
        for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) {
            for (bd_size_t j = 0; j < error_scale; j++) {
                if ((0xff & rand()) != read_block[i*error_scale + j]) {
                    error_mask[i/8] |= 1 << (i%8);
                }
            }
        }

        // Check that the data was unmodified
        srand(seed);
        for (bd_size_t i = 0; i < block_size; i++) {
            TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
        }
    }

    err = bd.deinit();
    TEST_ASSERT_EQUAL(0, err);
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
    GREENTEA_SETUP(30, "default_auto");
    return verbose_test_setup_handler(number_of_cases);
}

Case cases[] = {
    Case("Testing read write random blocks", test_read_write),
};

Specification specification(test_setup, cases);

int main() {
    return !Harness::run(specification);
}
<commit_msg>Reshuffled memory usage for heap block device tests on small targets<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"

#include "HeapBlockDevice.h"
#include <stdlib.h>

using namespace utest::v1;

#define TEST_BLOCK_SIZE 512
#if MBED_SMALL_TARGET
#    define TEST_BLOCK_DEVICE_SIZE 8*TEST_BLOCK_SIZE
#else
#    define TEST_BLOCK_DEVICE_SIZE 16*TEST_BLOCK_SIZE
#endif
#define TEST_BLOCK_COUNT 10
#define TEST_ERROR_MASK 16

const struct {
    const char *name;
    bd_size_t (BlockDevice::*method)() const;
} ATTRS[] = {
    {"read size",    &BlockDevice::get_read_size},
    {"program size", &BlockDevice::get_program_size},
    {"erase size",   &BlockDevice::get_erase_size},
    {"total size",   &BlockDevice::size},
};

// Simple test that read/writes random set of blocks
void test_read_write() {
    HeapBlockDevice bd(TEST_BLOCK_DEVICE_SIZE, TEST_BLOCK_SIZE);

    int err = bd.init();
    TEST_ASSERT_EQUAL(0, err);

    for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) {
        static const char *prefixes[] = {"", "k", "M", "G"};
        for (int i = 3; i >= 0; i--) {
            bd_size_t size = (bd.*ATTRS[a].method)();
            if (size >= (1ULL << 10*i)) {
                printf("%s: %llu%sbytes (%llubytes)\n",
                    ATTRS[a].name, size >> 10*i, prefixes[i], size);
                break;
            }
        }
    }

    bd_size_t block_size = bd.get_erase_size();
    uint8_t *write_block = new uint8_t[block_size];
    uint8_t *read_block = new uint8_t[block_size];
    uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK];
    unsigned addrwidth = ceil(log(float(bd.size()-1)) / log(float(16)))+1;

    for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
        // Find a random block
        bd_addr_t block = (rand()*block_size) % bd.size();

        // Use next random number as temporary seed to keep
        // the address progressing in the pseudorandom sequence
        unsigned seed = rand();

        // Fill with random sequence
        srand(seed);
        for (bd_size_t i = 0; i < block_size; i++) {
            write_block[i] = 0xff & rand();
        }

        // erase, program, and read the block
        printf("test  %0*llx:%llu...\n", addrwidth, block, block_size);

        err = bd.erase(block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        err = bd.program(write_block, block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        printf("write %0*llx:%llu ", addrwidth, block, block_size);
        for (int i = 0; i < 16; i++) {
            printf("%02x", write_block[i]);
        }
        printf("...\n");

        err = bd.read(read_block, block, block_size);
        TEST_ASSERT_EQUAL(0, err);

        printf("read  %0*llx:%llu ", addrwidth, block, block_size);
        for (int i = 0; i < 16; i++) {
            printf("%02x", read_block[i]);
        }
        printf("...\n");

        // Find error mask for debugging
        memset(error_mask, 0, TEST_ERROR_MASK);
        bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8);

        srand(seed);
        for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) {
            for (bd_size_t j = 0; j < error_scale; j++) {
                if ((0xff & rand()) != read_block[i*error_scale + j]) {
                    error_mask[i/8] |= 1 << (i%8);
                }
            }
        }

        printf("error %0*llx:%llu ", addrwidth, block, block_size);
        for (int i = 0; i < 16; i++) {
            printf("%02x", error_mask[i]);
        }
        printf("\n");

        // Check that the data was unmodified
        srand(seed);
        for (bd_size_t i = 0; i < block_size; i++) {
            TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
        }
    }

    err = bd.deinit();
    TEST_ASSERT_EQUAL(0, err);
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
    GREENTEA_SETUP(30, "default_auto");
    return verbose_test_setup_handler(number_of_cases);
}

Case cases[] = {
    Case("Testing read write random blocks", test_read_write),
};

Specification specification(test_setup, cases);

int main() {
    return !Harness::run(specification);
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  glShaderFactory.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "glShaderFactory.h"
#include "Render/Core/shader.h"
#include "Render/gl/glTypes.h"
#include "Render/gl/gl_impl.h"
#include "Core/String/StringBuilder.h"
#include "Core/Memory/Memory.h"

namespace Oryol {
namespace Render {

using namespace Core;
using namespace IO;
using namespace Resource;

//------------------------------------------------------------------------------
glShaderFactory::glShaderFactory() :
isValid(false) {
    // empty
}

//------------------------------------------------------------------------------
glShaderFactory::~glShaderFactory() {
    o_assert(!this->isValid);
}

//------------------------------------------------------------------------------
void
glShaderFactory::Setup() {
    o_assert(!this->isValid);
    this->isValid = true;
}

//------------------------------------------------------------------------------
void
glShaderFactory::Discard() {
    o_assert(this->isValid);
    this->isValid = false;
}

//------------------------------------------------------------------------------
bool
glShaderFactory::IsValid() const {
    return this->isValid;
}

//------------------------------------------------------------------------------
void
glShaderFactory::SetupResource(shader& shd) {
    o_assert(this->isValid);
    o_assert(shd.GetState() == State::Setup);
    ORYOL_GL_CHECK_ERROR();

    Log::Info("glShaderFactory: compiling shader '%s'\n", shd.GetSetup().GetLocator().Location().AsCStr());

    // create a shader object
    const ShaderSetup& setup = shd.GetSetup();
    GLuint glShader = this->compileShader(setup.GetType(), setup.GetSource(), setup.GetDefines());

    // if compilation has failed, stop the program
    if (0 == glShader) {
        o_error("Failed to compile shader '%s'\n", setup.GetLocator().Location().AsCStr());
        shd.setState(State::Failed);
        return;
    }

    // all ok, shader has been successfully compiled
    shd.setShaderType(setup.GetType());
    shd.glSetShader(glShader);
    shd.setState(State::Valid);
}

//------------------------------------------------------------------------------
void
glShaderFactory::DestroyResource(shader& shd) {
    o_assert(this->isValid);
    o_assert(State::Valid == shd.GetState());

    GLuint glShader = shd.glGetShader();
    o_assert(0 != glShader);
    glDeleteShader(glShader);
    ORYOL_GL_CHECK_ERROR();

    shd.clear();
    shd.setState(State::Setup);
}

//------------------------------------------------------------------------------
GLuint
glShaderFactory::compileShader(ShaderType::Code type, const String& src, const Map<String,String>& defines) const {
    o_assert(src.IsValid());

    GLenum glShaderType = glTypes::AsGLShaderType(type);
    GLuint glShader = glCreateShader(glShaderType);
    o_assert(0 != glShader);
    ORYOL_GL_CHECK_ERROR();

    // estimate length of complete source string
    int32 srcLength = 0;
    for (const auto& define : defines) {
        srcLength += 10 + define.Key().Length() + define.Value().Length();
    }
    srcLength += src.Length();

    // setup the shader source
    StringBuilder strBuilder;
    strBuilder.Reserve(2 * srcLength + 1024);
    #if ORYOL_OPENGLES2
        strBuilder.Append("#define ORYOL_OPENGLES2 (1)\n");
        if (GL_VERTEX_SHADER == glShaderType) {
            strBuilder.Append("#define VS_INPUT(type,name) attribute type name\n");
            strBuilder.Append("#define VS_OUTPUT(type,name) varying type name\n");
        }
        if (GL_FRAGMENT_SHADER == glShaderType) {
            strBuilder.Append("precision mediump float;\n");
            strBuilder.Append("#define FS_INPUT(type,name) varying type name\n");
            strBuilder.Append("#define TEXTURE2D(x,y) texture2D(x,y)\n");
            strBuilder.Append("#define TEXTURECUBE(x,y) textureCube(x,y)\n");
            strBuilder.Append("#define FragmentColor gl_FragColor\n");
        }
    #else
        strBuilder.Append("#version 150\n");
        strBuilder.Append("#define ORYOL_OPENGL (1)\n");
        strBuilder.Append("#define lowp\n");
        strBuilder.Append("#define mediump\n");
        strBuilder.Append("#define highp\n");
        if (GL_VERTEX_SHADER == glShaderType) {
            strBuilder.Append("#define VS_INPUT(type,name) in type name\n");
            strBuilder.Append("#define VS_OUTPUT(type,name) out type name\n");
        }
        if (GL_FRAGMENT_SHADER == glShaderType) {
            strBuilder.Append("#define FS_INPUT(type,name) in type name\n");
            strBuilder.Append("#define TEXTURE2D(x,y) texture(x,y)\n");
            strBuilder.Append("#define TEXTURECUBE(x,y) texture(x,y)\n");
            strBuilder.Append("out vec4 FragmentColor;\n");
        }
    #endif

    #if ORYOL_WINDOWS
        strBuilder.Append("#define ORYOL_WINDOWS (1)\n");
    #elif ORYOL_MACOS
        strBuilder.Append("#define ORYOL_MACOS (1)\n");
    #elif ORYOL_IOS
        strBuilder.Append("#define ORYOL_IOS (1)\n");
    #elif ORYOL_EMSCRIPTEN
        strBuilder.Append("#define ORYOL_EMSCRIPTEN (1)\n");
    #elif ORYOL_PNACL
        strBuilder.Append("#define ORYOL_PNACL (1)\n");
    #elif ORYOL_ANDROID
        strBuilder.Append("#define ORYOL_ANDROID (1)\n");
    #elif ORYOL_LINUX
        strBuilder.Append("#define ORYOL_LINUX (1)\n");
    #endif
    for (int32 i = 0; i < defines.Size(); i++) {
        const String& curDefineKey = defines.KeyAtIndex(i);
        const String& curDefineVal = defines.ValueAtIndex(i);
        strBuilder.Append("#define ");
        strBuilder.Append(curDefineKey);
        strBuilder.Append(' ');
        strBuilder.Append(curDefineVal);
        strBuilder.Append('\n');
    }
    strBuilder.Append(src);

    // attach source to shader object
    const GLchar* sourceString = strBuilder.AsCStr();
    const int sourceLength = strBuilder.Length();
    glShaderSource(glShader, 1, &sourceString, &sourceLength);
    ORYOL_GL_CHECK_ERROR();

    // compile the shader
    ::glCompileShader(glShader);
    ORYOL_GL_CHECK_ERROR();

    // compilation failed?
    GLint compileStatus = 0;
    glGetShaderiv(glShader, GL_COMPILE_STATUS, &compileStatus);
    ORYOL_GL_CHECK_ERROR();

    #if ORYOL_DEBUG
        GLint logLength = 0;
        glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &logLength);
        ORYOL_GL_CHECK_ERROR();
        if (logLength > 0) {

            // first print the shader source
            Log::Info("SHADER SOURCE:\n%s\n\n", sourceString);

            // now print the info log
            GLchar* shdLogBuf = (GLchar*) Memory::Alloc(logLength);
            glGetShaderInfoLog(glShader, logLength, &logLength, shdLogBuf);
            ORYOL_GL_CHECK_ERROR();
            Log::Info("SHADER LOG: %s\n\n", shdLogBuf);
            Memory::Free(shdLogBuf);
        }
    #endif

    if (!compileStatus) {
        // compiling failed
        glDeleteShader(glShader);
        ORYOL_GL_CHECK_ERROR();
        glShader = 0;
    }
    return glShader;
}

} // namespace Render
} // namespace Oryol
<commit_msg>GLSL doesn't require shader version 150 on Windows<commit_after>//------------------------------------------------------------------------------
//  glShaderFactory.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "glShaderFactory.h"
#include "Render/Core/shader.h"
#include "Render/gl/glTypes.h"
#include "Render/gl/gl_impl.h"
#include "Core/String/StringBuilder.h"
#include "Core/Memory/Memory.h"

namespace Oryol {
namespace Render {

using namespace Core;
using namespace IO;
using namespace Resource;

//------------------------------------------------------------------------------
glShaderFactory::glShaderFactory() :
isValid(false) {
    // empty
}

//------------------------------------------------------------------------------
glShaderFactory::~glShaderFactory() {
    o_assert(!this->isValid);
}

//------------------------------------------------------------------------------
void
glShaderFactory::Setup() {
    o_assert(!this->isValid);
    this->isValid = true;
}

//------------------------------------------------------------------------------
void
glShaderFactory::Discard() {
    o_assert(this->isValid);
    this->isValid = false;
}

//------------------------------------------------------------------------------
bool
glShaderFactory::IsValid() const {
    return this->isValid;
}

//------------------------------------------------------------------------------
void
glShaderFactory::SetupResource(shader& shd) {
    o_assert(this->isValid);
    o_assert(shd.GetState() == State::Setup);
    ORYOL_GL_CHECK_ERROR();

    Log::Info("glShaderFactory: compiling shader '%s'\n", shd.GetSetup().GetLocator().Location().AsCStr());

    // create a shader object
    const ShaderSetup& setup = shd.GetSetup();
    GLuint glShader = this->compileShader(setup.GetType(), setup.GetSource(), setup.GetDefines());

    // if compilation has failed, stop the program
    if (0 == glShader) {
        o_error("Failed to compile shader '%s'\n", setup.GetLocator().Location().AsCStr());
        shd.setState(State::Failed);
        return;
    }

    // all ok, shader has been successfully compiled
    shd.setShaderType(setup.GetType());
    shd.glSetShader(glShader);
    shd.setState(State::Valid);
}

//------------------------------------------------------------------------------
void
glShaderFactory::DestroyResource(shader& shd) {
    o_assert(this->isValid);
    o_assert(State::Valid == shd.GetState());

    GLuint glShader = shd.glGetShader();
    o_assert(0 != glShader);
    glDeleteShader(glShader);
    ORYOL_GL_CHECK_ERROR();

    shd.clear();
    shd.setState(State::Setup);
}

//------------------------------------------------------------------------------
GLuint
glShaderFactory::compileShader(ShaderType::Code type, const String& src, const Map<String,String>& defines) const {
    o_assert(src.IsValid());

    GLenum glShaderType = glTypes::AsGLShaderType(type);
    GLuint glShader = glCreateShader(glShaderType);
    o_assert(0 != glShader);
    ORYOL_GL_CHECK_ERROR();

    // estimate length of complete source string
    int32 srcLength = 0;
    for (const auto& define : defines) {
        srcLength += 10 + define.Key().Length() + define.Value().Length();
    }
    srcLength += src.Length();

    // setup the shader source
    StringBuilder strBuilder;
    strBuilder.Reserve(2 * srcLength + 1024);
    #if ORYOL_OPENGLES2
        strBuilder.Append("#define ORYOL_OPENGLES2 (1)\n");
        if (GL_VERTEX_SHADER == glShaderType) {
            strBuilder.Append("#define VS_INPUT(type,name) attribute type name\n");
            strBuilder.Append("#define VS_OUTPUT(type,name) varying type name\n");
        }
        if (GL_FRAGMENT_SHADER == glShaderType) {
            strBuilder.Append("precision mediump float;\n");
            strBuilder.Append("#define FS_INPUT(type,name) varying type name\n");
            strBuilder.Append("#define TEXTURE2D(x,y) texture2D(x,y)\n");
            strBuilder.Append("#define TEXTURECUBE(x,y) textureCube(x,y)\n");
            strBuilder.Append("#define FragmentColor gl_FragColor\n");
        }
    #elif ORYOL_OSX
        // FIXME: this is actually the OpenGL 3.x Core Profile
        strBuilder.Append("#version 150\n");
        strBuilder.Append("#define ORYOL_OPENGL (1)\n");
        strBuilder.Append("#define lowp\n");
        strBuilder.Append("#define mediump\n");
        strBuilder.Append("#define highp\n");
        if (GL_VERTEX_SHADER == glShaderType) {
            strBuilder.Append("#define VS_INPUT(type,name) in type name\n");
            strBuilder.Append("#define VS_OUTPUT(type,name) out type name\n");
        }
        if (GL_FRAGMENT_SHADER == glShaderType) {
            strBuilder.Append("#define FS_INPUT(type,name) in type name\n");
            strBuilder.Append("#define TEXTURE2D(x,y) texture(x,y)\n");
            strBuilder.Append("#define TEXTURECUBE(x,y) texture(x,y)\n");
            strBuilder.Append("out vec4 FragmentColor;\n");
        }
    #else
        strBuilder.Append("#define ORYOL_OPENGL (1)\n");
        strBuilder.Append("#define lowp\n");
        strBuilder.Append("#define mediump\n");
        strBuilder.Append("#define highp\n");
        if (GL_VERTEX_SHADER == glShaderType) {
            strBuilder.Append("#define VS_INPUT(type,name) attribute type name\n");
            strBuilder.Append("#define VS_OUTPUT(type,name) varying type name\n");
        }
        if (GL_FRAGMENT_SHADER == glShaderType) {
            strBuilder.Append("#define FS_INPUT(type,name) varying type name\n");
            strBuilder.Append("#define TEXTURE2D(x,y) texture2D(x,y)\n");
            strBuilder.Append("#define TEXTURECUBE(x,y) textureCube(x,y)\n");
            strBuilder.Append("#define FragmentColor gl_FragColor\n");
        }
    #endif

    #if ORYOL_WINDOWS
        strBuilder.Append("#define ORYOL_WINDOWS (1)\n");
    #elif ORYOL_MACOS
        strBuilder.Append("#define ORYOL_MACOS (1)\n");
    #elif ORYOL_IOS
        strBuilder.Append("#define ORYOL_IOS (1)\n");
    #elif ORYOL_EMSCRIPTEN
        strBuilder.Append("#define ORYOL_EMSCRIPTEN (1)\n");
    #elif ORYOL_PNACL
        strBuilder.Append("#define ORYOL_PNACL (1)\n");
    #elif ORYOL_ANDROID
        strBuilder.Append("#define ORYOL_ANDROID (1)\n");
    #elif ORYOL_LINUX
        strBuilder.Append("#define ORYOL_LINUX (1)\n");
    #endif
    for (int32 i = 0; i < defines.Size(); i++) {
        const String& curDefineKey = defines.KeyAtIndex(i);
        const String& curDefineVal = defines.ValueAtIndex(i);
        strBuilder.Append("#define ");
        strBuilder.Append(curDefineKey);
        strBuilder.Append(' ');
        strBuilder.Append(curDefineVal);
        strBuilder.Append('\n');
    }
    strBuilder.Append(src);

    // attach source to shader object
    const GLchar* sourceString = strBuilder.AsCStr();
    const int sourceLength = strBuilder.Length();
    glShaderSource(glShader, 1, &sourceString, &sourceLength);
    ORYOL_GL_CHECK_ERROR();

    // compile the shader
    ::glCompileShader(glShader);
    ORYOL_GL_CHECK_ERROR();

    // compilation failed?
    GLint compileStatus = 0;
    glGetShaderiv(glShader, GL_COMPILE_STATUS, &compileStatus);
    ORYOL_GL_CHECK_ERROR();

    #if ORYOL_DEBUG
        GLint logLength = 0;
        glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &logLength);
        ORYOL_GL_CHECK_ERROR();
        if (logLength > 0) {

            // first print the shader source
            Log::Info("SHADER SOURCE:\n%s\n\n", sourceString);

            // now print the info log
            GLchar* shdLogBuf = (GLchar*) Memory::Alloc(logLength);
            glGetShaderInfoLog(glShader, logLength, &logLength, shdLogBuf);
            ORYOL_GL_CHECK_ERROR();
            Log::Info("SHADER LOG: %s\n\n", shdLogBuf);
            Memory::Free(shdLogBuf);
        }
    #endif

    if (!compileStatus) {
        // compiling failed
        glDeleteShader(glShader);
        ORYOL_GL_CHECK_ERROR();
        glShader = 0;
    }
    return glShader;
}

} // namespace Render
} // namespace Oryol
<|endoftext|>


<commit_before>#include "comm.h"
#include "obj_base_weapon.h"
#include "being.h"

// Previously we used item VNUM to control affects of different deikhan swords
// Hoping to acheive similar results using item levels to control which affects are active
// TBaseWeapon::damageLevel()
// Avenger = 31.75
// Vindicator = 40.75
// Devasator = 52.75
//
// Additionally char levels are used to differentiate the strength of affects. (No twinked nebies)
// This seems more fair and also doesn't require a code change to add a new deikhan sword

// heal ser for avenger, heal crit for vindicator, heal for devastator
void doHeal(TBeing *ch, TObj *o) {
  int hp;

  // Get char level to control strength of heal
  int charLevel = ch->GetMaxLevel();

  act("$n's $o emanates a gentle <r>w<R>a<Y>rm<R>t<1><r>h.<1>",
      0, ch, o, 0, TO_ROOM);
  act("Your $o emanates a gentle <r>w<R>a<Y>rm<R>t<1><r>h.<1>",
      0, ch, o, 0, TO_CHAR);

  if (charLevel >= 45) {
    // Devastator level heals
    act("$n glows briefly with an ultramarine hue<1>.", FALSE, ch, NULL, 0, TO_ROOM);
    act("You glow briefly with an ultramarine hue<1>.", FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL, 50, 100);
  } else if (charLevel >= 30) {
    // Vindicator level heals
    colorAct(COLOR_SPELLS, "$n glows briefly with an
indigo hue<1>.",FALSE, ch, NULL, 0, TO_ROOM);
    colorAct(COLOR_SPELLS, "You glow briefly with an
indigo hue<1>.",FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL_CRITICAL, 50, 100);
  } else {
    // Avenger level heals
    colorAct(COLOR_SPELLS, "$n glows briefly with a blue hue<z>.", FALSE, ch, NULL, 0, TO_ROOM);
    colorAct(COLOR_SPELLS, "You glow briefly with a blue hue<z>.", FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL_SERIOUS, 50, 100);
  }

  ch->addToHit(hp);
  ch->updatePos();
}

void doBlind(TBeing *ch, TBeing *vict, TObj *o) {
  TBaseWeapon *tWeap;

  if (!(tWeap = dynamic_cast<TBaseWeapon *>(o)) || !vict)
    return;

  if (vict->affectedBySpell(SPELL_BLINDNESS) ||
      vict->isAffected(AFF_TRUE_SIGHT) ||
      vict->isAffected(AFF_CLARITY) ||
      ch->isNotPowerful(vict, (int)tWeap->weaponLevel(), SPELL_BLINDNESS, SILENT_YES))
    return;

  act("A searing light shines from $p, blinding $N.",
      FALSE, ch, o, vict, TO_CHAR);
  act("$n shields $s eyes as a searing light shines from $p, blinding $N.",
      FALSE, ch, o, vict, TO_NOTVICT);
  act("The world goes white then black as a searing light shines from $n's $p.",
      FALSE, ch, o, vict, TO_VICT);

  int       tDuration = (int)(tWeap->weaponLevel() * Pulse::UPDATES_PER_MUDHOUR);
  saveTypeT tSave     = SAVE_NO;

  vict->rawBlind((int)tWeap->weaponLevel(), tDuration, tSave);
}

void doSanc(TBeing *ch, TObj *o) {
  affectedData aff;
  int level=30;

  aff.type = SPELL_SANCTUARY;
  aff.level = level;
  aff.duration = Pulse::UPDATES_PER_MUDHOUR/4;
  aff.location = APPLY_PROTECTION;
  aff.modifier = level;
  aff.bitvector = AFF_SANCTUARY;

  act("$n's $o <W>flashes brightly<1>!",
      0, ch, o, 0, TO_ROOM);
  act("Your $o <W>flashes brightly<1>!",
      0, ch, o, 0, TO_CHAR);

  ch->affectJoin(ch, &aff, AVG_DUR_NO, AVG_EFF_YES);
}

int doHarm(TBeing *ch, TBeing *vict, TObj *o) {
  int rc=0;

  // Get char level to control strength of harm
  int charLevel = ch->GetMaxLevel();

  // Dam is random between 1 and 2-10 depending on char level
  int dam = ::number(1, max(2, (int)(charLevel / 5)));

  act("$n's $o projects righteous <Y>fury<1> into $N.",
      0, ch, o, vict, TO_ROOM);
  act("Your $o projects righteous <Y>fury<1> into $N.",
      0, ch, o, vict, TO_CHAR);

  rc = ch->reconcileDamage(vict, dam, DAMAGE_DRAIN);
  if (rc == -1)
    return DELETE_VICT;
  return TRUE;
}

int deikhanSword(TBeing *vict, cmdTypeT cmd, const char *arg, TObj *o, TObj *) {
  TBeing *ch;

  TBaseWeapon *tWeap;
  if (!(tWeap = dynamic_cast<TBaseWeapon *>(o)))
    return FALSE;
  // damageLevel of the weapon (devastator is 52)
  int weaponLevel=tWeap->damageLevel();

  if (!(ch = dynamic_cast<TBeing *>(o->equippedBy)))
    return FALSE;       // weapon not equipped (carried or on ground)

  if(cmd == CMD_GENERIC_PULSE){
    if(!::number(0,99) && weaponLevel >= 45){
      doSanc(ch, o);
      return TRUE;
    }

    if(!::number(0,49)){
      doHeal(ch, o);
      return TRUE;
    }
    return TRUE;
  }

  if (cmd == CMD_OBJ_HIT && vict){
    if(!::number(0,24) && weaponLevel >= 40){
      doBlind(ch, vict, o);
      return TRUE;
    }

    if(!::number(0,3)){
      return doHarm(ch, vict, o);
    }
    return TRUE;
  }

  return FALSE;
}

<commit_msg>Update Sanct<commit_after>#include "comm.h"
#include "obj_base_weapon.h"
#include "being.h"

// Previously we used item VNUM to control affects of different deikhan swords
// Hoping to acheive similar results using item levels to control which affects are active
// TBaseWeapon::damageLevel()
// Avenger = 31.75
// Vindicator = 40.75
// Devasator = 52.75
//
// Additionally char levels are used to differentiate the strength of affects. (No twinked nebies)
// This seems more fair and also doesn't require a code change to add a new deikhan sword

// heal ser for avenger, heal crit for vindicator, heal for devastator
void doHeal(TBeing *ch, TObj *o) {
  int hp;

  // Get char level to control strength of heal
  int charLevel = ch->GetMaxLevel();

  act("$n's $o emanates a gentle <r>w<R>a<Y>rm<R>t<1><r>h.<1>",
      0, ch, o, 0, TO_ROOM);
  act("Your $o emanates a gentle <r>w<R>a<Y>rm<R>t<1><r>h.<1>",
      0, ch, o, 0, TO_CHAR);

  if (charLevel >= 45) {
    // Devastator level heals
    act("$n glows briefly with an ultramarine hue<1>.", FALSE, ch, NULL, 0, TO_ROOM);
    act("You glow briefly with an ultramarine hue<1>.", FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL, 50, 100);
  } else if (charLevel >= 30) {
    // Vindicator level heals
    colorAct(COLOR_SPELLS, "$n glows briefly with an
indigo hue<1>.",FALSE, ch, NULL, 0, TO_ROOM);
    colorAct(COLOR_SPELLS, "You glow briefly with an
indigo hue<1>.",FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL_CRITICAL, 50, 100);
  } else {
    // Avenger level heals
    colorAct(COLOR_SPELLS, "$n glows briefly with a blue hue<z>.", FALSE, ch, NULL, 0, TO_ROOM);
    colorAct(COLOR_SPELLS, "You glow briefly with a blue hue<z>.", FALSE, ch, NULL, 0, TO_CHAR);

    hp = ch->getSkillDam(ch, SPELL_HEAL_SERIOUS, 50, 100);
  }

  ch->addToHit(hp);
  ch->updatePos();
}

void doBlind(TBeing *ch, TBeing *vict, TObj *o) {
  TBaseWeapon *tWeap;

  if (!(tWeap = dynamic_cast<TBaseWeapon *>(o)) || !vict)
    return;

  if (vict->affectedBySpell(SPELL_BLINDNESS) ||
      vict->isAffected(AFF_TRUE_SIGHT) ||
      vict->isAffected(AFF_CLARITY) ||
      ch->isNotPowerful(vict, (int)tWeap->weaponLevel(), SPELL_BLINDNESS, SILENT_YES))
    return;

  act("A searing light shines from $p, blinding $N.",
      FALSE, ch, o, vict, TO_CHAR);
  act("$n shields $s eyes as a searing light shines from $p, blinding $N.",
      FALSE, ch, o, vict, TO_NOTVICT);
  act("The world goes white then black as a searing light shines from $n's $p.",
      FALSE, ch, o, vict, TO_VICT);

  int       tDuration = (int)(tWeap->weaponLevel() * Pulse::UPDATES_PER_MUDHOUR);
  saveTypeT tSave     = SAVE_NO;

  vict->rawBlind((int)tWeap->weaponLevel(), tDuration, tSave);
}

void doSanc(TBeing *ch, TObj *o) {
  affectedData aff;
  int level=50;

  aff.type = SPELL_SANCTUARY;
  aff.level = level;
  aff.duration = Pulse::ONE_SECOND * 60 * 8;
  aff.location = APPLY_PROTECTION;
  aff.modifier = level;
  aff.bitvector = AFF_SANCTUARY;

  act("$n's $o <W>flashes brightly<1>!",
      0, ch, o, 0, TO_ROOM);
  act("Your $o <W>flashes brightly<1>!",
      0, ch, o, 0, TO_CHAR);

  ch->affectJoin(ch, &aff, AVG_DUR_NO, AVG_EFF_YES);
}

int doHarm(TBeing *ch, TBeing *vict, TObj *o) {
  int rc=0;

  // Get char level to control strength of harm
  int charLevel = ch->GetMaxLevel();

  // Dam is random between 1 and 2-10 depending on char level
  int dam = ::number(1, max(2, (int)(charLevel / 5)));

  act("$n's $o projects righteous <Y>fury<1> into $N.",
      0, ch, o, vict, TO_ROOM);
  act("Your $o projects righteous <Y>fury<1> into $N.",
      0, ch, o, vict, TO_CHAR);

  rc = ch->reconcileDamage(vict, dam, DAMAGE_DRAIN);
  if (rc == -1)
    return DELETE_VICT;
  return TRUE;
}

int deikhanSword(TBeing *vict, cmdTypeT cmd, const char *arg, TObj *o, TObj *) {
  TBeing *ch;

  TBaseWeapon *tWeap;
  if (!(tWeap = dynamic_cast<TBaseWeapon *>(o)))
    return FALSE;
  // damageLevel of the weapon (devastator is 52)
  int weaponLevel=tWeap->damageLevel();

  if (!(ch = dynamic_cast<TBeing *>(o->equippedBy)))
    return FALSE;       // weapon not equipped (carried or on ground)

  if(cmd == CMD_GENERIC_PULSE){
    if(!::number(0,99) && weaponLevel >= 45){
      doSanc(ch, o);
      return TRUE;
    }

    if(!::number(0,49)){
      doHeal(ch, o);
      return TRUE;
    }
    return TRUE;
  }

  if (cmd == CMD_OBJ_HIT && vict){
    if(!::number(0,24) && weaponLevel >= 40){
      doBlind(ch, vict, o);
      return TRUE;
    }

    if(!::number(0,3)){
      return doHarm(ch, vict, o);
    }
    return TRUE;
  }

  return FALSE;
}

<|endoftext|>