blob: 73b0c0314ee5c0add7a3a5c477857f8d86481e08 [file] [log] [blame]
//
// Copyright 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/ImmutableStringBuilder.h"
#include "compiler/translator/UtilsHLSL.h"
namespace sh
{
// static
ImmutableString ImageFunctionHLSL::GetImageReference(
TInfoSinkBase &out,
const ImageFunctionHLSL::ImageFunction &imageFunction)
{
static const ImmutableString kImageIndexStr("[index]");
if (imageFunction.readonly)
{
static const ImmutableString kReadonlyImagesStr("readonlyImages");
ImmutableString suffix(
TextureGroupSuffix(imageFunction.image, imageFunction.imageInternalFormat));
out << " const uint index = imageIndex - readonlyImageIndexOffset" << suffix.data()
<< ";\n";
ImmutableStringBuilder imageRefBuilder(kReadonlyImagesStr.length() + suffix.length() +
kImageIndexStr.length());
imageRefBuilder << kReadonlyImagesStr << suffix << kImageIndexStr;
return imageRefBuilder;
}
else
{
static const ImmutableString kImagesStr("images");
ImmutableString suffix(
RWTextureGroupSuffix(imageFunction.image, imageFunction.imageInternalFormat));
out << " const uint index = imageIndex - imageIndexOffset" << suffix.data() << ";\n";
ImmutableStringBuilder imageRefBuilder(kImagesStr.length() + suffix.length() +
kImageIndexStr.length());
imageRefBuilder << kImagesStr << suffix << kImageIndexStr;
return imageRefBuilder;
}
}
void ImageFunctionHLSL::OutputImageFunctionArgumentList(
TInfoSinkBase &out,
const ImageFunctionHLSL::ImageFunction &imageFunction)
{
out << "uint imageIndex";
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 ImmutableString &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 ImmutableString &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 ImmutableString &imageReference)
{
if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
IsImage2D(imageFunction.image) || IsImageCube(imageFunction.image))
{
out << " " << imageReference << "[p] = data;\n";
}
else
UNREACHABLE();
}
ImmutableString ImageFunctionHLSL::ImageFunction::name() const
{
static const ImmutableString kGlImageName("gl_image");
ImmutableString suffix(nullptr);
if (readonly)
{
suffix = ImmutableString(TextureTypeSuffix(image, imageInternalFormat));
}
else
{
suffix = ImmutableString(RWTextureTypeSuffix(image, imageInternalFormat));
}
ImmutableStringBuilder name(kGlImageName.length() + suffix.length() + 5u);
name << kGlImageName << suffix;
switch (method)
{
case Method::SIZE:
name << "Size";
break;
case Method::LOAD:
name << "Load";
break;
case Method::STORE:
name << "Store";
break;
default:
UNREACHABLE();
}
return name;
}
ImageFunctionHLSL::ImageFunction::DataType ImageFunctionHLSL::ImageFunction::getDataType(
TLayoutImageInternalFormat format) const
{
switch (format)
{
case EiifRGBA32F:
case EiifRGBA16F:
case EiifR32F:
return ImageFunction::DataType::FLOAT4;
case EiifRGBA32UI:
case EiifRGBA16UI:
case EiifRGBA8UI:
case EiifR32UI:
return ImageFunction::DataType::UINT4;
case EiifRGBA32I:
case EiifRGBA16I:
case EiifRGBA8I:
case EiifR32I:
return ImageFunction::DataType::INT4;
case EiifRGBA8:
return ImageFunction::DataType::UNORM_FLOAT4;
case EiifRGBA8_SNORM:
return ImageFunction::DataType::SNORM_FLOAT4;
default:
UNREACHABLE();
}
return ImageFunction::DataType::NONE;
}
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, type, method, readonly) <
std::tie(rhs.image, rhs.type, rhs.method, rhs.readonly);
}
ImmutableString ImageFunctionHLSL::useImageFunction(const ImmutableString &name,
const TBasicType &type,
TLayoutImageInternalFormat imageInternalFormat,
bool readonly)
{
ASSERT(IsImage(type));
ImageFunction imageFunction;
imageFunction.image = type;
imageFunction.imageInternalFormat = imageInternalFormat;
imageFunction.readonly = readonly;
imageFunction.type = imageFunction.getDataType(imageInternalFormat);
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)
{
// Skip to generate image2D functions here, dynamically generate these
// functions when linking, or after dispatch or draw.
if (IsImage2D(imageFunction.image))
{
mUsedImage2DFunctionNames.insert(imageFunction.name().data());
continue;
}
// Function header
out << imageFunction.getReturnType() << " " << imageFunction.name() << "(";
OutputImageFunctionArgumentList(out, imageFunction);
out << ")\n"
"{\n";
ImmutableString imageReference = GetImageReference(out, imageFunction);
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