blob: c2eeeac591970fb66bea739d48c5eb3d30ca65ab [file] [log] [blame]
//
// Copyright 2018 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.
//
// ImageCopy.frag: Copy parts of an image to another.
#version 450 core
#extension GL_EXT_samplerless_texture_functions : require
#define MAKE_SRC_RESOURCE(prefix, type) prefix ## type
#if SrcIsFloat
#define SRC_RESOURCE(type) type
#define SrcType vec4
#elif SrcIsSint
#define SRC_RESOURCE(type) MAKE_SRC_RESOURCE(i, type)
#define SrcType ivec4
#elif SrcIsUint
#define SRC_RESOURCE(type) MAKE_SRC_RESOURCE(u, type)
#define SrcType uvec4
#else
#error "Not all source formats are accounted for"
#endif
#if SrcIs2D
#define SRC_RESOURCE_NAME texture2D
#elif SrcIs2DArray
#define SRC_RESOURCE_NAME texture2DArray
#elif SrcIs3D
#define SRC_RESOURCE_NAME texture3D
#elif SrcIsYUV
#define SRC_RESOURCE_NAME sampler2D
#else
#error "Not all source types are accounted for"
#endif
#if DestIsFloat
#define DestType vec4
#elif DestIsSint
#define DestType ivec4
#elif DestIsUint
#define DestType uvec4
#else
#error "Not all destination formats are accounted for"
#endif
layout(set = 0, binding = 0) uniform SRC_RESOURCE(SRC_RESOURCE_NAME) src;
layout(location = 0) out DestType dst;
layout(push_constant) uniform PushConstants {
// Translation from source to destination coordinates.
ivec2 srcOffset;
ivec2 dstOffset;
int srcMip;
int srcLayer;
// Whether x and/or y need to be flipped
bool flipX;
bool flipY;
// Premultiplied alpha conversions
bool premultiplyAlpha;
bool unmultiplyAlpha;
// Whether destination is emulated luminance/alpha.
bool dstHasLuminance;
bool dstIsAlpha;
// Whether source or destination are sRGB. They are brought to linear space for alpha
// premultiply/unmultiply, as well as to ensure the copy doesn't change values due to sRGB
// transformation.
bool srcIsSRGB;
bool dstIsSRGB;
// Bits 0~3 tell whether R,G,B or A exist in destination, but as a result of format emulation.
// Bit 0 is ignored, because R is always present. For B and G, the result is set to 0 and for
// A, the result is set to 1.
int dstDefaultChannelsMask;
bool rotateXY;
} params;
#if SrcIsFloat
float linearToSRGB(float linear)
{
// sRGB transform: y = sRGB(x) where x is linear and y is the sRGB encoding:
//
// x <= 0.0031308: y = x * 12.92
// o.w. : y = 1.055 * x^(1/2.4) - 0.055
if (linear <= 0.0031308)
{
return linear * 12.92;
}
else
{
return pow(linear, (1.0f / 2.4f)) * 1.055f - 0.055f;
}
}
#endif
#if DestIsFloat
float sRGBToLinear(float sRGB)
{
// sRGB inverse transform: x = sRGB^(-1)(y) where x is linear and y is the sRGB encoding:
//
// y <= 0.04045: x = y / 12.92
// o.w. : x = ((y + 0.055) / 1.055)^(2.4)
if (sRGB <= 0.04045)
{
return sRGB / 12.92;
}
else
{
return pow((sRGB + 0.055f) / 1.055f, 2.4f);
}
}
#endif
void main()
{
ivec2 dstSubImageCoords = ivec2(gl_FragCoord.xy) - params.dstOffset;
ivec2 srcSubImageCoords = dstSubImageCoords;
// If flipping X and/or Y, srcOffset would contain the opposite x and/or y coordinate, so we
// can simply reverse the direction in which x and/or y grows.
if (params.flipX)
{
srcSubImageCoords.x = -srcSubImageCoords.x;
}
if (params.flipY)
{
srcSubImageCoords.y = -srcSubImageCoords.y;
}
if (params.rotateXY)
{
srcSubImageCoords.xy = srcSubImageCoords.yx;
}
#if SrcIs2D
SrcType srcValue = texelFetch(src, params.srcOffset + srcSubImageCoords, params.srcMip);
#elif SrcIs2DArray || SrcIs3D
SrcType srcValue = texelFetch(src, ivec3(params.srcOffset + srcSubImageCoords, params.srcLayer), params.srcMip);
#elif SrcIsYUV
SrcType srcValue = texture(
src, vec2(params.srcOffset + srcSubImageCoords) / textureSize(src, 0), params.srcMip);
#else
#error "Not all source types are accounted for"
#endif
// Note: sRGB formats are unorm, so SrcIsFloat must be necessarily set
#if SrcIsFloat
if (params.srcIsSRGB)
{
// If src is sRGB, then texelFetch has performed an sRGB->linear transformation. We need to
// undo that to get back to the original values in the texture. This is done to avoid
// creating a non-sRGB view of the texture, which would require recreating it with the
// VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag.
srcValue.r = linearToSRGB(srcValue.r);
srcValue.g = linearToSRGB(srcValue.g);
srcValue.b = linearToSRGB(srcValue.b);
}
#endif
if (params.premultiplyAlpha)
{
srcValue.rgb *= srcValue.a;
}
else if (params.unmultiplyAlpha && srcValue.a > 0)
{
srcValue.rgb /= srcValue.a;
}
#if SrcIsFloat && !DestIsFloat
srcValue *= 255.0;
#endif
// Convert value to destination type.
DestType dstValue = DestType(srcValue);
#if !SrcIsFloat && DestIsFloat
dstValue /= 255.0;
#endif
// Note: sRGB formats are unorm, so DestIsFloat must be necessarily set
#if DestIsFloat
if (params.dstIsSRGB)
{
// If dst is sRGB, then export will perform a linear->sRGB transformation. We need to
// preemptively undo that so the values will be exported unchanged.This is done to avoid
// creating a non-sRGB view of the texture, which would require recreating it with the
// VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT flag.
dstValue.r = sRGBToLinear(dstValue.r);
dstValue.g = sRGBToLinear(dstValue.g);
dstValue.b = sRGBToLinear(dstValue.b);
}
#endif
// If dst is luminance/alpha, it's implemented with R or RG. Do the appropriate swizzle.
if (params.dstHasLuminance)
{
dstValue.rg = dstValue.ra;
}
else if (params.dstIsAlpha)
{
dstValue.r = dstValue.a;
}
else
{
int defaultChannelsMask = params.dstDefaultChannelsMask;
if ((defaultChannelsMask & 2) != 0)
{
dstValue.g = 0;
}
if ((defaultChannelsMask & 4) != 0)
{
dstValue.b = 0;
}
if ((defaultChannelsMask & 8) != 0)
{
dstValue.a = 1;
}
}
dst = dstValue;
}