ES31: Support images in the compiler on D3D backend.

BUG=angleproject:1987
TEST=angle_end2end_tests

Change-Id: I83f5f9ffda7e676a8f98b963d1f1c50e9463faf4
Reviewed-on: https://chromium-review.googlesource.com/706247
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/ImageFunctionHLSL.cpp b/src/compiler/translator/ImageFunctionHLSL.cpp
new file mode 100644
index 0000000..40b5e1f
--- /dev/null
+++ b/src/compiler/translator/ImageFunctionHLSL.cpp
@@ -0,0 +1,304 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ImageFunctionHLSL: Class for writing implementations of ESSL image functions into HLSL output.
+//
+
+#include "compiler/translator/ImageFunctionHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+// static
+void ImageFunctionHLSL::OutputImageFunctionArgumentList(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction)
+{
+    if (imageFunction.readonly)
+    {
+        out << TextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+    }
+    else
+    {
+        out << RWTextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+    }
+
+    if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::LOAD ||
+        imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+    {
+        switch (imageFunction.image)
+        {
+            case EbtImage2D:
+            case EbtIImage2D:
+            case EbtUImage2D:
+                out << ", int2 p";
+                break;
+            case EbtImage3D:
+            case EbtIImage3D:
+            case EbtUImage3D:
+            case EbtImageCube:
+            case EbtIImageCube:
+            case EbtUImageCube:
+            case EbtImage2DArray:
+            case EbtIImage2DArray:
+            case EbtUImage2DArray:
+                out << ", int3 p";
+                break;
+            default:
+                UNREACHABLE();
+        }
+
+        if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+        {
+            switch (imageFunction.image)
+            {
+                case EbtImage2D:
+                case EbtImage3D:
+                case EbtImageCube:
+                case EbtImage2DArray:
+                    out << ", float4 data";
+                    break;
+                case EbtIImage2D:
+                case EbtIImage3D:
+                case EbtIImageCube:
+                case EbtIImage2DArray:
+                    out << ", int4 data";
+                    break;
+                case EbtUImage2D:
+                case EbtUImage3D:
+                case EbtUImageCube:
+                case EbtUImage2DArray:
+                    out << ", uint4 data";
+                    break;
+                default:
+                    UNREACHABLE();
+            }
+        }
+    }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageSizeFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImageCube(imageFunction.image))
+    {
+        // "depth" stores either the number of layers in an array texture or 3D depth
+        out << "    uint width; uint height; uint depth;\n"
+            << "    " << imageReference << ".GetDimensions(width, height, depth);\n";
+    }
+    else if (IsImage2D(imageFunction.image))
+    {
+        out << "    uint width; uint height;\n"
+            << "    " << imageReference << ".GetDimensions(width, height);\n";
+    }
+    else
+        UNREACHABLE();
+
+    if (strcmp(imageFunction.getReturnType(), "int3") == 0)
+    {
+        out << "    return int3(width, height, depth);\n";
+    }
+    else
+    {
+        out << "    return int2(width, height);\n";
+    }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageLoadFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImageCube(imageFunction.image))
+    {
+        out << "    return " << imageReference << "[uint3(p.x, p.y, p.z)];\n";
+    }
+    else if (IsImage2D(imageFunction.image))
+    {
+        out << "    return " << imageReference << "[uint2(p.x, p.y)];\n";
+    }
+    else
+        UNREACHABLE();
+}
+
+// static
+void ImageFunctionHLSL::OutputImageStoreFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImage2D(imageFunction.image) || IsImageCube(imageFunction.image))
+    {
+        out << "    " << imageReference << "[p] = data;\n";
+    }
+    else
+        UNREACHABLE();
+}
+
+TString ImageFunctionHLSL::ImageFunction::name() const
+{
+    TString name = "gl_image";
+    if (readonly)
+    {
+        name += TextureTypeSuffix(image, imageInternalFormat);
+    }
+    else
+    {
+        name += RWTextureTypeSuffix(image, imageInternalFormat);
+    }
+
+    switch (method)
+    {
+        case Method::SIZE:
+            name += "Size";
+            break;
+        case Method::LOAD:
+            name += "Load";
+            break;
+        case Method::STORE:
+            name += "Store";
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return name;
+}
+
+const char *ImageFunctionHLSL::ImageFunction::getReturnType() const
+{
+    if (method == ImageFunction::Method::SIZE)
+    {
+        switch (image)
+        {
+            case EbtImage2D:
+            case EbtIImage2D:
+            case EbtUImage2D:
+            case EbtImageCube:
+            case EbtIImageCube:
+            case EbtUImageCube:
+                return "int2";
+            case EbtImage3D:
+            case EbtIImage3D:
+            case EbtUImage3D:
+            case EbtImage2DArray:
+            case EbtIImage2DArray:
+            case EbtUImage2DArray:
+                return "int3";
+            default:
+                UNREACHABLE();
+        }
+    }
+    else if (method == ImageFunction::Method::LOAD)
+    {
+        switch (image)
+        {
+            case EbtImage2D:
+            case EbtImage3D:
+            case EbtImageCube:
+            case EbtImage2DArray:
+                return "float4";
+            case EbtIImage2D:
+            case EbtIImage3D:
+            case EbtIImageCube:
+            case EbtIImage2DArray:
+                return "int4";
+            case EbtUImage2D:
+            case EbtUImage3D:
+            case EbtUImageCube:
+            case EbtUImage2DArray:
+                return "uint4";
+            default:
+                UNREACHABLE();
+        }
+    }
+    else if (method == ImageFunction::Method::STORE)
+    {
+        return "void";
+    }
+    else
+    {
+        UNREACHABLE();
+    }
+    return "";
+}
+
+bool ImageFunctionHLSL::ImageFunction::operator<(const ImageFunction &rhs) const
+{
+    return std::tie(image, imageInternalFormat, readonly, method) <
+           std::tie(rhs.image, rhs.imageInternalFormat, rhs.readonly, rhs.method);
+}
+
+TString ImageFunctionHLSL::useImageFunction(const TString &name,
+                                            const TBasicType &type,
+                                            TLayoutImageInternalFormat imageInternalFormat,
+                                            bool readonly)
+{
+    ASSERT(IsImage(type));
+    ImageFunction imageFunction;
+    imageFunction.image               = type;
+    imageFunction.imageInternalFormat = imageInternalFormat;
+    imageFunction.readonly            = readonly;
+
+    if (name == "imageSize")
+    {
+        imageFunction.method = ImageFunction::Method::SIZE;
+    }
+    else if (name == "imageLoad")
+    {
+        imageFunction.method = ImageFunction::Method::LOAD;
+    }
+    else if (name == "imageStore")
+    {
+        imageFunction.method = ImageFunction::Method::STORE;
+    }
+    else
+        UNREACHABLE();
+
+    mUsesImage.insert(imageFunction);
+    return imageFunction.name();
+}
+
+void ImageFunctionHLSL::imageFunctionHeader(TInfoSinkBase &out)
+{
+    for (const ImageFunction &imageFunction : mUsesImage)
+    {
+        // Function header
+        out << imageFunction.getReturnType() << " " << imageFunction.name() << "(";
+
+        OutputImageFunctionArgumentList(out, imageFunction);
+
+        out << ")\n"
+               "{\n";
+
+        TString imageReference("tex");
+
+        if (imageFunction.method == ImageFunction::Method::SIZE)
+        {
+            OutputImageSizeFunctionBody(out, imageFunction, imageReference);
+        }
+        else if (imageFunction.method == ImageFunction::Method::LOAD)
+        {
+            OutputImageLoadFunctionBody(out, imageFunction, imageReference);
+        }
+        else
+        {
+            OutputImageStoreFunctionBody(out, imageFunction, imageReference);
+        }
+
+        out << "}\n"
+               "\n";
+    }
+}
+
+}  // namespace sh