|  | // Copyright 2016 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 "Sampler.hpp" | 
|  |  | 
|  | #include "Context.hpp" | 
|  | #include "Pipeline/PixelRoutine.hpp" | 
|  | #include "Vulkan/VkDebug.hpp" | 
|  | #include "Vulkan/VkImage.hpp" | 
|  |  | 
|  | #include <cstring> | 
|  |  | 
|  | namespace sw | 
|  | { | 
|  | FilterType Sampler::maximumTextureFilterQuality = FILTER_LINEAR; | 
|  | MipmapType Sampler::maximumMipmapFilterQuality = MIPMAP_POINT; | 
|  |  | 
|  | Sampler::State::State() | 
|  | { | 
|  | memset(this, 0, sizeof(State)); | 
|  | } | 
|  |  | 
|  | Sampler::Sampler() | 
|  | { | 
|  | // FIXME: Mipmap::init | 
|  | static const unsigned int zero = 0x00FF00FF; | 
|  |  | 
|  | for(int level = 0; level < MIPMAP_LEVELS; level++) | 
|  | { | 
|  | Mipmap &mipmap = texture.mipmap[level]; | 
|  |  | 
|  | memset(&mipmap, 0, sizeof(Mipmap)); | 
|  |  | 
|  | for(int face = 0; face < 6; face++) | 
|  | { | 
|  | mipmap.buffer[face] = &zero; | 
|  | } | 
|  | } | 
|  |  | 
|  | textureFormat = VK_FORMAT_UNDEFINED; | 
|  | textureType = TEXTURE_NULL; | 
|  |  | 
|  | textureFilter = FILTER_LINEAR; | 
|  | addressingModeU = ADDRESSING_WRAP; | 
|  | addressingModeV = ADDRESSING_WRAP; | 
|  | addressingModeW = ADDRESSING_WRAP; | 
|  | mipmapFilterState = MIPMAP_NONE; | 
|  | sRGB = false; | 
|  | gather = false; | 
|  | highPrecisionFiltering = false; | 
|  | border = 0; | 
|  |  | 
|  | swizzleR = SWIZZLE_RED; | 
|  | swizzleG = SWIZZLE_GREEN; | 
|  | swizzleB = SWIZZLE_BLUE; | 
|  | swizzleA = SWIZZLE_ALPHA; | 
|  |  | 
|  | compare = COMPARE_BYPASS; | 
|  |  | 
|  | texture.LOD = 0.0f; | 
|  | exp2LOD = 1.0f; | 
|  |  | 
|  | texture.baseLevel = 0; | 
|  | texture.maxLevel = 1000; | 
|  | texture.maxLod = MAX_TEXTURE_LOD; | 
|  | texture.minLod = 0; | 
|  | } | 
|  |  | 
|  | Sampler::~Sampler() | 
|  | { | 
|  | } | 
|  |  | 
|  | Sampler::State Sampler::samplerState() const | 
|  | { | 
|  | State state; | 
|  |  | 
|  | if(textureType != TEXTURE_NULL) | 
|  | { | 
|  | state.textureType = textureType; | 
|  | state.textureFormat = textureFormat; | 
|  | state.textureFilter = getTextureFilter(); | 
|  | state.addressingModeU = getAddressingModeU(); | 
|  | state.addressingModeV = getAddressingModeV(); | 
|  | state.addressingModeW = getAddressingModeW(); | 
|  | state.mipmapFilter = mipmapFilter(); | 
|  | state.sRGB = (sRGB && textureFormat.isSRGBreadable()) || textureFormat.isSRGBformat(); | 
|  | state.swizzleR = swizzleR; | 
|  | state.swizzleG = swizzleG; | 
|  | state.swizzleB = swizzleB; | 
|  | state.swizzleA = swizzleA; | 
|  | state.highPrecisionFiltering = highPrecisionFiltering; | 
|  | state.compare = getCompareFunc(); | 
|  |  | 
|  | #if PERF_PROFILE | 
|  | state.compressedFormat = Surface::isCompressed(externalTextureFormat); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | return state; | 
|  | } | 
|  |  | 
|  | void Sampler::setTextureLevel(int face, int level, vk::Image *image, TextureType type) | 
|  | { | 
|  | if(image) | 
|  | { | 
|  | Mipmap &mipmap = texture.mipmap[level]; | 
|  |  | 
|  | border = image->isCube() ? 1 : 0; | 
|  | VkImageSubresourceLayers subresourceLayers = | 
|  | { | 
|  | VK_IMAGE_ASPECT_COLOR_BIT, | 
|  | static_cast<uint32_t>(level), | 
|  | static_cast<uint32_t>(face), | 
|  | 1 | 
|  | }; | 
|  | mipmap.buffer[face] = image->getTexelPointer({ -border, -border, 0 }, subresourceLayers); | 
|  |  | 
|  | if(face == 0) | 
|  | { | 
|  | VkImageAspectFlagBits aspect = VK_IMAGE_ASPECT_COLOR_BIT; // FIXME: get proper aspect | 
|  | textureFormat = image->getFormat(aspect); | 
|  |  | 
|  | VkExtent3D mipLevelExtent = image->getMipLevelExtent(level); | 
|  | int width = mipLevelExtent.width; | 
|  | int height = mipLevelExtent.height; | 
|  | int depth = mipLevelExtent.depth; | 
|  | int pitchP = image->rowPitchBytes(aspect, level); | 
|  | int sliceP = image->slicePitchBytes(aspect, level); | 
|  |  | 
|  | if(level == 0) | 
|  | { | 
|  | texture.widthHeightLOD[0] = width * exp2LOD; | 
|  | texture.widthHeightLOD[1] = width * exp2LOD; | 
|  | texture.widthHeightLOD[2] = height * exp2LOD; | 
|  | texture.widthHeightLOD[3] = height * exp2LOD; | 
|  |  | 
|  | texture.widthLOD[0] = width * exp2LOD; | 
|  | texture.widthLOD[1] = width * exp2LOD; | 
|  | texture.widthLOD[2] = width * exp2LOD; | 
|  | texture.widthLOD[3] = width * exp2LOD; | 
|  |  | 
|  | texture.heightLOD[0] = height * exp2LOD; | 
|  | texture.heightLOD[1] = height * exp2LOD; | 
|  | texture.heightLOD[2] = height * exp2LOD; | 
|  | texture.heightLOD[3] = height * exp2LOD; | 
|  |  | 
|  | texture.depthLOD[0] = depth * exp2LOD; | 
|  | texture.depthLOD[1] = depth * exp2LOD; | 
|  | texture.depthLOD[2] = depth * exp2LOD; | 
|  | texture.depthLOD[3] = depth * exp2LOD; | 
|  | } | 
|  |  | 
|  | if(textureFormat.isFloatFormat()) | 
|  | { | 
|  | mipmap.fWidth[0] = (float)width / 65536.0f; | 
|  | mipmap.fWidth[1] = (float)width / 65536.0f; | 
|  | mipmap.fWidth[2] = (float)width / 65536.0f; | 
|  | mipmap.fWidth[3] = (float)width / 65536.0f; | 
|  |  | 
|  | mipmap.fHeight[0] = (float)height / 65536.0f; | 
|  | mipmap.fHeight[1] = (float)height / 65536.0f; | 
|  | mipmap.fHeight[2] = (float)height / 65536.0f; | 
|  | mipmap.fHeight[3] = (float)height / 65536.0f; | 
|  |  | 
|  | mipmap.fDepth[0] = (float)depth / 65536.0f; | 
|  | mipmap.fDepth[1] = (float)depth / 65536.0f; | 
|  | mipmap.fDepth[2] = (float)depth / 65536.0f; | 
|  | mipmap.fDepth[3] = (float)depth / 65536.0f; | 
|  | } | 
|  |  | 
|  | short halfTexelU = 0x8000 / width; | 
|  | short halfTexelV = 0x8000 / height; | 
|  | short halfTexelW = 0x8000 / depth; | 
|  |  | 
|  | mipmap.uHalf[0] = halfTexelU; | 
|  | mipmap.uHalf[1] = halfTexelU; | 
|  | mipmap.uHalf[2] = halfTexelU; | 
|  | mipmap.uHalf[3] = halfTexelU; | 
|  |  | 
|  | mipmap.vHalf[0] = halfTexelV; | 
|  | mipmap.vHalf[1] = halfTexelV; | 
|  | mipmap.vHalf[2] = halfTexelV; | 
|  | mipmap.vHalf[3] = halfTexelV; | 
|  |  | 
|  | mipmap.wHalf[0] = halfTexelW; | 
|  | mipmap.wHalf[1] = halfTexelW; | 
|  | mipmap.wHalf[2] = halfTexelW; | 
|  | mipmap.wHalf[3] = halfTexelW; | 
|  |  | 
|  | mipmap.width[0] = width; | 
|  | mipmap.width[1] = width; | 
|  | mipmap.width[2] = width; | 
|  | mipmap.width[3] = width; | 
|  |  | 
|  | mipmap.height[0] = height; | 
|  | mipmap.height[1] = height; | 
|  | mipmap.height[2] = height; | 
|  | mipmap.height[3] = height; | 
|  |  | 
|  | mipmap.depth[0] = depth; | 
|  | mipmap.depth[1] = depth; | 
|  | mipmap.depth[2] = depth; | 
|  | mipmap.depth[3] = depth; | 
|  |  | 
|  | mipmap.onePitchP[0] = 1; | 
|  | mipmap.onePitchP[1] = pitchP; | 
|  | mipmap.onePitchP[2] = 1; | 
|  | mipmap.onePitchP[3] = pitchP; | 
|  |  | 
|  | mipmap.pitchP[0] = pitchP; | 
|  | mipmap.pitchP[1] = pitchP; | 
|  | mipmap.pitchP[2] = pitchP; | 
|  | mipmap.pitchP[3] = pitchP; | 
|  |  | 
|  | mipmap.sliceP[0] = sliceP; | 
|  | mipmap.sliceP[1] = sliceP; | 
|  | mipmap.sliceP[2] = sliceP; | 
|  | mipmap.sliceP[3] = sliceP; | 
|  |  | 
|  | if(textureFormat.hasYuvFormat()) | 
|  | { | 
|  | unsigned int YStride = pitchP; | 
|  | unsigned int YSize = YStride * height; | 
|  | unsigned int CStride = align<16>(YStride / 2); | 
|  | unsigned int CSize = CStride * height / 2; | 
|  |  | 
|  | mipmap.buffer[1] = (byte*)mipmap.buffer[0] + YSize; | 
|  | mipmap.buffer[2] = (byte*)mipmap.buffer[1] + CSize; | 
|  |  | 
|  | texture.mipmap[1].width[0] = width / 2; | 
|  | texture.mipmap[1].width[1] = width / 2; | 
|  | texture.mipmap[1].width[2] = width / 2; | 
|  | texture.mipmap[1].width[3] = width / 2; | 
|  | texture.mipmap[1].height[0] = height / 2; | 
|  | texture.mipmap[1].height[1] = height / 2; | 
|  | texture.mipmap[1].height[2] = height / 2; | 
|  | texture.mipmap[1].height[3] = height / 2; | 
|  | texture.mipmap[1].onePitchP[0] = 1; | 
|  | texture.mipmap[1].onePitchP[1] = CStride; | 
|  | texture.mipmap[1].onePitchP[2] = 1; | 
|  | texture.mipmap[1].onePitchP[3] = CStride; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | textureType = type; | 
|  | } | 
|  |  | 
|  | void Sampler::setTextureFilter(FilterType textureFilter) | 
|  | { | 
|  | this->textureFilter = (FilterType)min(textureFilter, maximumTextureFilterQuality); | 
|  | } | 
|  |  | 
|  | void Sampler::setMipmapFilter(MipmapType mipmapFilter) | 
|  | { | 
|  | mipmapFilterState = (MipmapType)min(mipmapFilter, maximumMipmapFilterQuality); | 
|  | } | 
|  |  | 
|  | void Sampler::setGatherEnable(bool enable) | 
|  | { | 
|  | gather = enable; | 
|  | } | 
|  |  | 
|  | void Sampler::setAddressingModeU(AddressingMode addressingMode) | 
|  | { | 
|  | addressingModeU = addressingMode; | 
|  | } | 
|  |  | 
|  | void Sampler::setAddressingModeV(AddressingMode addressingMode) | 
|  | { | 
|  | addressingModeV = addressingMode; | 
|  | } | 
|  |  | 
|  | void Sampler::setAddressingModeW(AddressingMode addressingMode) | 
|  | { | 
|  | addressingModeW = addressingMode; | 
|  | } | 
|  |  | 
|  | void Sampler::setReadSRGB(bool sRGB) | 
|  | { | 
|  | this->sRGB = sRGB; | 
|  | } | 
|  |  | 
|  | void Sampler::setBorderColor(const Color<float> &borderColor) | 
|  | { | 
|  | // FIXME: Compact into generic function   // FIXME: Clamp | 
|  | short r = iround(0xFFFF * borderColor.r); | 
|  | short g = iround(0xFFFF * borderColor.g); | 
|  | short b = iround(0xFFFF * borderColor.b); | 
|  | short a = iround(0xFFFF * borderColor.a); | 
|  |  | 
|  | texture.borderColor4[0][0] = texture.borderColor4[0][1] = texture.borderColor4[0][2] = texture.borderColor4[0][3] = r; | 
|  | texture.borderColor4[1][0] = texture.borderColor4[1][1] = texture.borderColor4[1][2] = texture.borderColor4[1][3] = g; | 
|  | texture.borderColor4[2][0] = texture.borderColor4[2][1] = texture.borderColor4[2][2] = texture.borderColor4[2][3] = b; | 
|  | texture.borderColor4[3][0] = texture.borderColor4[3][1] = texture.borderColor4[3][2] = texture.borderColor4[3][3] = a; | 
|  |  | 
|  | texture.borderColorF[0][0] = texture.borderColorF[0][1] = texture.borderColorF[0][2] = texture.borderColorF[0][3] = borderColor.r; | 
|  | texture.borderColorF[1][0] = texture.borderColorF[1][1] = texture.borderColorF[1][2] = texture.borderColorF[1][3] = borderColor.g; | 
|  | texture.borderColorF[2][0] = texture.borderColorF[2][1] = texture.borderColorF[2][2] = texture.borderColorF[2][3] = borderColor.b; | 
|  | texture.borderColorF[3][0] = texture.borderColorF[3][1] = texture.borderColorF[3][2] = texture.borderColorF[3][3] = borderColor.a; | 
|  | } | 
|  |  | 
|  | void Sampler::setMaxAnisotropy(float maxAnisotropy) | 
|  | { | 
|  | texture.maxAnisotropy = maxAnisotropy; | 
|  | } | 
|  |  | 
|  | void Sampler::setHighPrecisionFiltering(bool highPrecisionFiltering) | 
|  | { | 
|  | this->highPrecisionFiltering = highPrecisionFiltering; | 
|  | } | 
|  |  | 
|  | void Sampler::setSwizzleR(SwizzleType swizzleR) | 
|  | { | 
|  | this->swizzleR = swizzleR; | 
|  | } | 
|  |  | 
|  | void Sampler::setSwizzleG(SwizzleType swizzleG) | 
|  | { | 
|  | this->swizzleG = swizzleG; | 
|  | } | 
|  |  | 
|  | void Sampler::setSwizzleB(SwizzleType swizzleB) | 
|  | { | 
|  | this->swizzleB = swizzleB; | 
|  | } | 
|  |  | 
|  | void Sampler::setSwizzleA(SwizzleType swizzleA) | 
|  | { | 
|  | this->swizzleA = swizzleA; | 
|  | } | 
|  |  | 
|  | void Sampler::setCompareFunc(CompareFunc compare) | 
|  | { | 
|  | this->compare = compare; | 
|  | } | 
|  |  | 
|  | void Sampler::setBaseLevel(int baseLevel) | 
|  | { | 
|  | texture.baseLevel = baseLevel; | 
|  | } | 
|  |  | 
|  | void Sampler::setMaxLevel(int maxLevel) | 
|  | { | 
|  | texture.maxLevel = maxLevel; | 
|  | } | 
|  |  | 
|  | void Sampler::setMinLod(float minLod) | 
|  | { | 
|  | texture.minLod = clamp(minLod, 0.0f, (float)(MAX_TEXTURE_LOD)); | 
|  | } | 
|  |  | 
|  | void Sampler::setMaxLod(float maxLod) | 
|  | { | 
|  | texture.maxLod = clamp(maxLod, 0.0f, (float)(MAX_TEXTURE_LOD)); | 
|  | } | 
|  |  | 
|  | void Sampler::setFilterQuality(FilterType maximumFilterQuality) | 
|  | { | 
|  | Sampler::maximumTextureFilterQuality = maximumFilterQuality; | 
|  | } | 
|  |  | 
|  | void Sampler::setMipmapQuality(MipmapType maximumFilterQuality) | 
|  | { | 
|  | Sampler::maximumMipmapFilterQuality = maximumFilterQuality; | 
|  | } | 
|  |  | 
|  | void Sampler::setMipmapLOD(float LOD) | 
|  | { | 
|  | texture.LOD = LOD; | 
|  | exp2LOD = exp2(LOD); | 
|  | } | 
|  |  | 
|  | bool Sampler::hasTexture() const | 
|  | { | 
|  | return textureType != TEXTURE_NULL; | 
|  | } | 
|  |  | 
|  | bool Sampler::hasUnsignedTexture() const | 
|  | { | 
|  | return textureFormat.isUnsignedComponent(0) && | 
|  | textureFormat.isUnsignedComponent(1) && | 
|  | textureFormat.isUnsignedComponent(2) && | 
|  | textureFormat.isUnsignedComponent(3); | 
|  | } | 
|  |  | 
|  | bool Sampler::hasCubeTexture() const | 
|  | { | 
|  | return textureType == TEXTURE_CUBE; | 
|  | } | 
|  |  | 
|  | bool Sampler::hasVolumeTexture() const | 
|  | { | 
|  | return textureType == TEXTURE_3D || textureType == TEXTURE_2D_ARRAY; | 
|  | } | 
|  |  | 
|  | const Texture &Sampler::getTextureData() | 
|  | { | 
|  | return texture; | 
|  | } | 
|  |  | 
|  | MipmapType Sampler::mipmapFilter() const | 
|  | { | 
|  | if(mipmapFilterState != MIPMAP_NONE) | 
|  | { | 
|  | for(int i = 1; i < MIPMAP_LEVELS; i++) | 
|  | { | 
|  | if(texture.mipmap[0].buffer[0] != texture.mipmap[i].buffer[0]) | 
|  | { | 
|  | return mipmapFilterState; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Only one mipmap level | 
|  | return MIPMAP_NONE; | 
|  | } | 
|  |  | 
|  | TextureType Sampler::getTextureType() const | 
|  | { | 
|  | return textureType; | 
|  | } | 
|  |  | 
|  | FilterType Sampler::getTextureFilter() const | 
|  | { | 
|  | // Don't filter 1x1 textures. | 
|  | if(texture.mipmap[0].width[0] == 1 && texture.mipmap[0].height[0] == 1 && texture.mipmap[0].depth[0] == 1) | 
|  | { | 
|  | if(mipmapFilter() == MIPMAP_NONE) | 
|  | { | 
|  | return FILTER_POINT; | 
|  | } | 
|  | } | 
|  |  | 
|  | FilterType filter = textureFilter; | 
|  |  | 
|  | if(gather && textureFormat.componentCount() == 1) | 
|  | { | 
|  | filter = FILTER_GATHER; | 
|  | } | 
|  |  | 
|  | if(textureType != TEXTURE_2D || texture.maxAnisotropy == 1.0f) | 
|  | { | 
|  | return (FilterType)min(filter, FILTER_LINEAR); | 
|  | } | 
|  |  | 
|  | return filter; | 
|  | } | 
|  |  | 
|  | AddressingMode Sampler::getAddressingModeU() const | 
|  | { | 
|  | if(textureType == TEXTURE_CUBE) | 
|  | { | 
|  | return border ? ADDRESSING_SEAMLESS : ADDRESSING_CLAMP; | 
|  | } | 
|  |  | 
|  | return addressingModeU; | 
|  | } | 
|  |  | 
|  | AddressingMode Sampler::getAddressingModeV() const | 
|  | { | 
|  | if(textureType == TEXTURE_CUBE) | 
|  | { | 
|  | return border ? ADDRESSING_SEAMLESS : ADDRESSING_CLAMP; | 
|  | } | 
|  |  | 
|  | return addressingModeV; | 
|  | } | 
|  |  | 
|  | AddressingMode Sampler::getAddressingModeW() const | 
|  | { | 
|  | if(textureType == TEXTURE_2D_ARRAY || | 
|  | textureType == TEXTURE_2D || | 
|  | textureType == TEXTURE_CUBE || | 
|  | textureType == TEXTURE_RECTANGLE) | 
|  | { | 
|  | return ADDRESSING_LAYER; | 
|  | } | 
|  |  | 
|  | return addressingModeW; | 
|  | } | 
|  |  | 
|  | CompareFunc Sampler::getCompareFunc() const | 
|  | { | 
|  | if(getTextureFilter() == FILTER_GATHER) | 
|  | { | 
|  | return COMPARE_BYPASS; | 
|  | } | 
|  |  | 
|  | return compare; | 
|  | } | 
|  | } |