Dynamic dispatch for image samplers

Bug: b/130746922
Change-Id: I35dfcb93c8e902d0be566fad50ae991d5490e853
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29348
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj b/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj
index 0216c93..3d2d37a 100644
--- a/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj
+++ b/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj
@@ -242,6 +242,7 @@
     <ClInclude Include="$(SolutionDir)src\Pipeline\SpirvID.hpp" />

     <ClCompile Include="$(SolutionDir)src\Pipeline\SpirvShader.cpp" />

     <ClInclude Include="$(SolutionDir)src\Pipeline\SpirvShader.hpp" />

+    <ClCompile Include="$(SolutionDir)src\Pipeline\SpirvShaderSampling.cpp" />

     <ClCompile Include="$(SolutionDir)src\Pipeline\SpirvShader_dbg.cpp" />

     <ClCompile Include="$(SolutionDir)src\Pipeline\VertexProgram.cpp" />

     <ClInclude Include="$(SolutionDir)src\Pipeline\VertexProgram.hpp" />

diff --git a/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj.filters b/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj.filters
index 6d4eb0e..bc7a75c 100644
--- a/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj.filters
+++ b/build/Visual Studio 15 2017 Win64/libvk_swiftshader.vcxproj.filters
@@ -79,6 +79,9 @@
     <ClCompile Include="$(SolutionDir)src\Pipeline\SpirvShader_dbg.cpp">

       <Filter>src\Pipeline</Filter>

     </ClCompile>

+    <ClCompile Include="$(SolutionDir)src\Pipeline\SpirvShaderSampling.cpp">

+      <Filter>src\Pipeline</Filter>

+    </ClCompile>

     <ClCompile Include="$(SolutionDir)src\Pipeline\VertexProgram.cpp">

       <Filter>src\Pipeline</Filter>

     </ClCompile>

diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index c5df163..50de374 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -4301,63 +4301,20 @@
 		auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
 		size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
 
-		const uint8_t *p = reinterpret_cast<const uint8_t*>(state->descriptorSets[d.DescriptorSet]) + bindingOffset;
-		const auto *t = reinterpret_cast<const vk::SampledImageDescriptor*>(p);
+		auto descriptor = state->routine->descriptorSets[d.DescriptorSet] + bindingOffset; // vk::SampledImageDescriptor*
+		auto sampler = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::SampledImageDescriptor, sampler)); // vk::Sampler*
+		auto imageView = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::SampledImageDescriptor, imageView)); // vk::ImageView*
 
-		Sampler::State samplerState;
-		samplerState.textureType = TEXTURE_2D;                  ASSERT(t->imageView->getType() == VK_IMAGE_VIEW_TYPE_2D);  // TODO(b/129523279)
-		samplerState.textureFormat = t->imageView->getFormat();
-		samplerState.textureFilter = FILTER_POINT;              ASSERT(t->sampler->magFilter == VK_FILTER_NEAREST); ASSERT(t->sampler->minFilter == VK_FILTER_NEAREST);  // TODO(b/129523279)
+		auto samplerFunc = Call(getImageSampler, imageView, sampler);
 
-		samplerState.addressingModeU = ADDRESSING_WRAP;         ASSERT(t->sampler->addressModeU == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
-		samplerState.addressingModeV = ADDRESSING_WRAP;         ASSERT(t->sampler->addressModeV == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
-		samplerState.addressingModeW = ADDRESSING_WRAP;         ASSERT(t->sampler->addressModeW == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
-		samplerState.mipmapFilter = MIPMAP_POINT;               ASSERT(t->sampler->mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST);  // TODO(b/129523279)
-		samplerState.sRGB = false;                              ASSERT(t->imageView->getFormat().isSRGBformat() == false);  // TODO(b/129523279)
-		samplerState.swizzleR = SWIZZLE_RED;                    ASSERT(t->imageView->getComponentMapping().r == VK_COMPONENT_SWIZZLE_R);  // TODO(b/129523279)
-		samplerState.swizzleG = SWIZZLE_GREEN;                  ASSERT(t->imageView->getComponentMapping().g == VK_COMPONENT_SWIZZLE_G);  // TODO(b/129523279)
-		samplerState.swizzleB = SWIZZLE_BLUE;                   ASSERT(t->imageView->getComponentMapping().b == VK_COMPONENT_SWIZZLE_B);  // TODO(b/129523279)
-		samplerState.swizzleA = SWIZZLE_ALPHA;                  ASSERT(t->imageView->getComponentMapping().a == VK_COMPONENT_SWIZZLE_A);  // TODO(b/129523279)
-		samplerState.highPrecisionFiltering = false;
-		samplerState.compare = COMPARE_BYPASS;                  ASSERT(t->sampler->compareEnable == VK_FALSE);  // TODO(b/129523279)
+		Array<SIMD::Float> in(2);
+		in[0] = coordinate.Float(0);
+		in[1] = coordinate.Float(1);
 
-	//	minLod  // TODO(b/129523279)
-	//	maxLod  // TODO(b/129523279)
-	//	borderColor  // TODO(b/129523279)
-		ASSERT(t->sampler->mipLodBias == 0.0f);  // TODO(b/129523279)
-		ASSERT(t->sampler->anisotropyEnable == VK_FALSE);  // TODO(b/129523279)
-		ASSERT(t->sampler->unnormalizedCoordinates == VK_FALSE);  // TODO(b/129523279)
+		Array<SIMD::Float> out(4);
+		Call<ImageSampler>(samplerFunc, sampledImage, &in[0], &out[0]);
 
-		SamplerCore sampler(constants, samplerState);
-
-		Pointer<Byte> texture = sampledImage + OFFSET(vk::SampledImageDescriptor, texture); // sw::Texture*
-		SIMD::Float u = coordinate.Float(0);
-		SIMD::Float v = coordinate.Float(1);
-		SIMD::Float w(0);     // TODO(b/129523279)
-		SIMD::Float q(0);     // TODO(b/129523279)
-		SIMD::Float bias(0);  // TODO(b/129523279)
-		Vector4f dsx;         // TODO(b/129523279)
-		Vector4f dsy;         // TODO(b/129523279)
-		Vector4f offset;      // TODO(b/129523279)
-		SamplerFunction samplerFunction = { Implicit, None };   ASSERT(insn.wordCount() == 5);  // TODO(b/129523279)
-
-		Vector4f sample = sampler.sampleTextureF(texture, u, v, w, q, bias, dsx, dsy, offset, samplerFunction);
-
-		if(getType(resultType.element).opcode() == spv::OpTypeFloat)
-		{
-			result.move(0, sample.x);
-			result.move(1, sample.y);
-			result.move(2, sample.z);
-			result.move(3, sample.w);
-		}
-		else
-		{
-			// TODO(b/129523279): Add a Sampler::sampleTextureI() method.
-			result.move(0, As<SIMD::Int>(sample.x * SIMD::Float(0xFF)));
-			result.move(1, As<SIMD::Int>(sample.y * SIMD::Float(0xFF)));
-			result.move(2, As<SIMD::Int>(sample.z * SIMD::Float(0xFF)));
-			result.move(3, As<SIMD::Int>(sample.w * SIMD::Float(0xFF)));
-		}
+		for (int i = 0; i < 4; i++) { result.move(i, out[i]); }
 
 		return EmitResult::Continue;
 	}
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 69dd01e..f8862b6 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -40,6 +40,8 @@
 namespace vk
 {
 	class PipelineLayout;
+	class ImageView;
+	class Sampler;
 } // namespace vk
 
 namespace sw
@@ -745,6 +747,13 @@
 		//   x = significand * 2^exponent
 		// Returns the pair <significand, exponent>
 		std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val) const;
+
+		using ImageSampler = void(void* image, void* uvsIn, void* texelOut);
+
+		static ImageSampler *getImageSampler(vk::ImageView *imageView, vk::Sampler *sampler);
+		static void emitSamplerFunction(
+			vk::ImageView *imageView, vk::Sampler *sampler,
+			Pointer<Byte> image, Pointer<SIMD::Float> in, Pointer<Byte> out);
 	};
 
 	class SpirvRoutine
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
new file mode 100644
index 0000000..e90aad0
--- /dev/null
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -0,0 +1,132 @@
+// 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(vk::ImageView *imageView, vk::Sampler *sampler)
+{
+    // TODO: Move somewhere sensible.
+    static std::unordered_map<uintptr_t, ImageSampler*> cache;
+    static std::mutex mutex;
+
+    // TODO: Don't use pointers they can be deleted and reused, combine some two
+    // unique ids.
+    auto key = reinterpret_cast<uintptr_t>(imageView) ^ reinterpret_cast<uintptr_t>(sampler);
+
+    std::unique_lock<std::mutex> lock(mutex);
+    auto it = cache.find(key);
+    if (it != cache.end()) { return it->second; }
+
+    // TODO: Hold a separate mutex lock for the sampler being built.
+    auto function = rr::Function<Void(Pointer<Byte> image, Pointer<SIMD::Float>, Pointer<SIMD::Float>)>();
+    Pointer<Byte> image = function.Arg<0>();
+    Pointer<SIMD::Float> in = function.Arg<1>();
+    Pointer<SIMD::Float> out = function.Arg<2>();
+    emitSamplerFunction(imageView, sampler, image, in, out);
+    auto fptr = reinterpret_cast<ImageSampler*>((void *)function("sampler")->getEntry());
+    cache.emplace(key, fptr);
+    return fptr;
+}
+
+void SpirvShader::emitSamplerFunction(
+        vk::ImageView *imageView, vk::Sampler *sampler,
+        Pointer<Byte> image, Pointer<SIMD::Float> in, Pointer<Byte> out)
+{
+    SIMD::Float u = in[0];
+    SIMD::Float v = in[1];
+
+    Pointer<Byte> constants;  // FIXME(b/129523279)
+
+    Sampler::State samplerState;
+    samplerState.textureType = TEXTURE_2D;                  ASSERT(imageView->getType() == VK_IMAGE_VIEW_TYPE_2D);  // TODO(b/129523279)
+    samplerState.textureFormat = imageView->getFormat();
+    samplerState.textureFilter = FILTER_POINT;              ASSERT(sampler->magFilter == VK_FILTER_NEAREST); ASSERT(sampler->minFilter == VK_FILTER_NEAREST);  // TODO(b/129523279)
+
+    samplerState.addressingModeU = ADDRESSING_WRAP;         ASSERT(sampler->addressModeU == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
+    samplerState.addressingModeV = ADDRESSING_WRAP;         ASSERT(sampler->addressModeV == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
+    samplerState.addressingModeW = ADDRESSING_WRAP;         ASSERT(sampler->addressModeW == VK_SAMPLER_ADDRESS_MODE_REPEAT);  // TODO(b/129523279)
+    samplerState.mipmapFilter = MIPMAP_POINT;               ASSERT(sampler->mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST);  // TODO(b/129523279)
+    samplerState.sRGB = false;                              ASSERT(imageView->getFormat().isSRGBformat() == false);  // TODO(b/129523279)
+    samplerState.swizzleR = SWIZZLE_RED;                    ASSERT(imageView->getComponentMapping().r == VK_COMPONENT_SWIZZLE_R);  // TODO(b/129523279)
+    samplerState.swizzleG = SWIZZLE_GREEN;                  ASSERT(imageView->getComponentMapping().g == VK_COMPONENT_SWIZZLE_G);  // TODO(b/129523279)
+    samplerState.swizzleB = SWIZZLE_BLUE;                   ASSERT(imageView->getComponentMapping().b == VK_COMPONENT_SWIZZLE_B);  // TODO(b/129523279)
+    samplerState.swizzleA = SWIZZLE_ALPHA;                  ASSERT(imageView->getComponentMapping().a == VK_COMPONENT_SWIZZLE_A);  // TODO(b/129523279)
+    samplerState.highPrecisionFiltering = false;
+    samplerState.compare = COMPARE_BYPASS;                  ASSERT(sampler->compareEnable == VK_FALSE);  // TODO(b/129523279)
+
+//	minLod  // TODO(b/129523279)
+//	maxLod  // TODO(b/129523279)
+//	borderColor  // TODO(b/129523279)
+    ASSERT(sampler->mipLodBias == 0.0f);  // TODO(b/129523279)
+    ASSERT(sampler->anisotropyEnable == VK_FALSE);  // TODO(b/129523279)
+    ASSERT(sampler->unnormalizedCoordinates == VK_FALSE);  // TODO(b/129523279)
+
+    SamplerCore s(constants, samplerState);
+
+    Pointer<Byte> texture = image + OFFSET(vk::SampledImageDescriptor, texture); // sw::Texture*
+    SIMD::Float w(0);     // TODO(b/129523279)
+    SIMD::Float q(0);     // TODO(b/129523279)
+    SIMD::Float bias(0);  // TODO(b/129523279)
+    Vector4f dsx;         // TODO(b/129523279)
+    Vector4f dsy;         // TODO(b/129523279)
+    Vector4f offset;      // TODO(b/129523279)
+    SamplerFunction samplerFunction = { Implicit, None };   // ASSERT(insn.wordCount() == 5);  // TODO(b/129523279)
+
+    Vector4f sample = s.sampleTextureF(texture, u, v, w, q, bias, dsx, dsy, offset, samplerFunction);
+
+    if(!vk::Format(imageView->getFormat()).isNonNormalizedInteger())
+    {
+        Pointer<SIMD::Float> rgba = out;
+        rgba[0] = sample.x;
+        rgba[1] = sample.y;
+        rgba[2] = sample.z;
+        rgba[3] = sample.w;
+    }
+    else
+    {
+        // TODO(b/129523279): Add a Sampler::sampleTextureI() method.
+        Pointer<SIMD::Int> rgba = out;
+        rgba[0] = As<SIMD::Int>(sample.x * SIMD::Float(0xFF));
+        rgba[1] = As<SIMD::Int>(sample.y * SIMD::Float(0xFF));
+        rgba[2] = As<SIMD::Int>(sample.z * SIMD::Float(0xFF));
+        rgba[3] = As<SIMD::Int>(sample.w * SIMD::Float(0xFF));
+    }
+}
+
+} // namespace sw
\ No newline at end of file
diff --git a/src/Vulkan/vulkan.vcxproj b/src/Vulkan/vulkan.vcxproj
index 59e8098..fa3a517 100644
--- a/src/Vulkan/vulkan.vcxproj
+++ b/src/Vulkan/vulkan.vcxproj
@@ -160,6 +160,7 @@
     <ClCompile Include="..\Pipeline\SetupRoutine.cpp" />

     <ClCompile Include="..\Pipeline\ShaderCore.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader.cpp" />

+    <ClCompile Include="..\Pipeline\SpirvShaderSampling.cpp" />

     <ClCompile Include="..\Pipeline\SpirvShader_dbg.cpp" />

     <ClCompile Include="..\Pipeline\VertexProgram.cpp" />

     <ClCompile Include="..\Pipeline\VertexRoutine.cpp" />

diff --git a/src/Vulkan/vulkan.vcxproj.filters b/src/Vulkan/vulkan.vcxproj.filters
index 9c99109..c95a986 100644
--- a/src/Vulkan/vulkan.vcxproj.filters
+++ b/src/Vulkan/vulkan.vcxproj.filters
@@ -252,6 +252,9 @@
     <ClCompile Include="..\Pipeline\SpirvShader.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>

+    <ClCompile Include="..\Pipeline\SpirvShaderSampling.cpp">

+      <Filter>Source Files\Pipeline</Filter>

+    </ClCompile>

     <ClCompile Include="..\Pipeline\SpirvShader_dbg.cpp">

       <Filter>Source Files\Pipeline</Filter>

     </ClCompile>