Commit 93560ef5 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Seamful cube map emulation

In GLSL, a cube texture is sampled with one of textureCube* functions. This function takes a 3D coordinate which is a vector from the center of the cube and identifies a direction to sample from. GLES2.0 has the following table that translates this 3D coordinate (Rx, Ry, Rz) to a face and ST coordinates within that face. This table can be found in Section 3.7.5 (Cube Map Texture Selection). A compiler pass is implemented in ANGLE that replaces samplerCube declarations with a sampler2DArray. The textureCube* functions are replaced with the corresponding texture* functions with the translated coordinates according to that table. Gradients provided to textureCubeGrad are translated using the same formulae, which is not precise but the spec specifies this projection to be implementation dependent. Helper invocations enabled through WQM (whole quad mode) cause a nuisance in that the extrapolated varyings used as coordinates in a textureCube call could have a different major axis (and therefore face) from the non-helper invocations that lie within the geometry. subgroupQuadSwap* operations are used in conjunction with gl_HelperInvocation to make sure the helper threads calculate texture UVs in the same face as the non-helper invocations. Bug: angleproject:3300 Bug: angleproject:3240 Bug: angleproject:3243 Bug: angleproject:3732 Change-Id: I0cb6a9b1f2e1e6a392b5baca1c7118ed1c502ccf Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1715977Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 44f518b5
......@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 210
#define ANGLE_SH_VERSION 211
enum ShShaderSpec
{
......@@ -287,6 +287,11 @@ const ShCompileOptions SH_FORCE_ATOMIC_VALUE_RESOLUTION = UINT64_C(1) << 42;
// Rewrite gl_BaseVertex and gl_BaseInstance as uniform int
const ShCompileOptions SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE = UINT64_C(1) << 43;
// Emulate seamful cube map sampling for OpenGL ES2.0. Currently only applies to the Vulkan
// backend, as subgroup operations are used. Once that dependency is broken, could be used with
// the other backends as well.
const ShCompileOptions SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING = UINT64_C(1) << 44;
// Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy
{
......
......@@ -194,6 +194,12 @@ struct FeaturesVk : FeatureSetBase
"On Pixel2, keep using transient vkCommandBuffer to work around driver issue in reseting"
"vkCommandBuffer",
&members, "http://b/135763283"};
// Seamful cube map emulation misbehaves on the AMD windows driver, so it's disallowed.
Feature disallowSeamfulCubeMapEmulation = {
"disallow_seamful_cube_map_emulation", FeatureCategory::VulkanWorkarounds,
"Seamful cube map emulation misbehaves on the AMD windows driver, so it's disallowed",
&members, "http://anglebug.com/3243"};
};
inline FeaturesVk::FeaturesVk() = default;
......
......@@ -422,6 +422,20 @@ bool IsSamplerType(GLenum type)
return false;
}
bool IsSamplerCubeType(GLenum type)
{
switch (type)
{
case GL_SAMPLER_CUBE:
case GL_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_SHADOW:
return true;
}
return false;
}
bool IsImageType(GLenum type)
{
switch (type)
......
......@@ -38,6 +38,7 @@ size_t VariableExternalSize(GLenum type);
int VariableRowCount(GLenum type);
int VariableColumnCount(GLenum type);
bool IsSamplerType(GLenum type);
bool IsSamplerCubeType(GLenum type);
bool IsImageType(GLenum type);
bool IsImage2DType(GLenum type);
bool IsAtomicCounterType(GLenum type);
......
......@@ -156,6 +156,8 @@ angle_translator_sources = [
"src/compiler/translator/tree_ops/RewriteAtomicCounters.h",
"src/compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.cpp",
"src/compiler/translator/tree_ops/RewriteAtomicFunctionExpressions.h",
"src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp",
"src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h",
"src/compiler/translator/tree_ops/RewriteDfdy.cpp",
"src/compiler/translator/tree_ops/RewriteDfdy.h",
"src/compiler/translator/tree_ops/RewriteDoWhile.cpp",
......
......@@ -18,6 +18,7 @@
#include "compiler/translator/StaticType.h"
#include "compiler/translator/tree_ops/NameEmbeddedUniformStructs.h"
#include "compiler/translator/tree_ops/RewriteAtomicCounters.h"
#include "compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h"
#include "compiler/translator/tree_ops/RewriteDfdy.h"
#include "compiler/translator/tree_ops/RewriteStructSamplers.h"
#include "compiler/translator/tree_util/BuiltIn_autogen.h"
......@@ -644,6 +645,11 @@ void TranslatorVulkan::translate(TIntermBlock *root,
sink << "#version 450 core\n";
if (compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING)
{
sink << "#extension GL_KHR_shader_subgroup_quad : require\n";
}
// Write out default uniforms into a uniform block assigned to a specific set/binding.
int defaultUniformCount = 0;
int structTypesUsedForUniforms = 0;
......@@ -680,6 +686,14 @@ void TranslatorVulkan::translate(TIntermBlock *root,
structTypesTraverser.updateTree();
}
// Rewrite samplerCubes as sampler2DArrays. This must be done after rewriting struct samplers
// as it doesn't expect that.
if (compileOptions & SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING)
{
RewriteCubeMapSamplersAs2DArray(root, &getSymbolTable(),
getShaderType() == GL_FRAGMENT_SHADER);
}
if (defaultUniformCount > 0)
{
sink << "\n@@ LAYOUT-defaultUniforms(std140) @@ uniform defaultUniforms\n{\n";
......
......@@ -329,6 +329,7 @@ class TType
void realize();
bool isSampler() const { return IsSampler(type); }
bool isSamplerCube() const { return type == EbtSamplerCube; }
bool isAtomicCounter() const { return IsAtomicCounter(type); }
private:
......
//
// Copyright 2019 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.
//
// RewriteCubeMapSamplersAs2DArray: Change samplerCube samplers to sampler2DArray, and the
// textureCube* function calls to calls to helper functions that select the cube map face and sample
// from the face as a 2D texture. This emulates seamful cube map sampling in ES2 (or desktop GL 2)
// and therefore only looks at samplerCube (i.e. not integer variants or cube arrays) and sampling
// functions that are defined in ES GLSL 1.0 (i.e. not the cube overload of texture()).
#ifndef COMPILER_TRANSLATOR_TREEOPS_REWRITECUBEMAPSAMPLERSAS2DARRAY_H_
#define COMPILER_TRANSLATOR_TREEOPS_REWRITECUBEMAPSAMPLERSAS2DARRAY_H_
namespace sh
{
class TIntermBlock;
class TSymbolTable;
void RewriteCubeMapSamplersAs2DArray(TIntermBlock *root,
TSymbolTable *symbolTable,
bool isFragmentShader);
} // namespace sh
#endif // COMPILER_TRANSLATOR_TREEOPS_REWRITECUBEMAPSAMPLERSAS2DARRAY_H_
......@@ -438,6 +438,8 @@ angle::Result ContextVk::initialize()
ANGLE_TRY(synchronizeCpuGpuTime());
}
mEmulateSeamfulCubeMapSampling = shouldEmulateSeamfulCubeMapSampling();
return angle::Result::Continue;
}
......@@ -2890,4 +2892,30 @@ vk::DescriptorSetLayoutDesc ContextVk::getDriverUniformsDescriptorSetDesc(
desc.update(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, shaderStages);
return desc;
}
bool ContextVk::shouldEmulateSeamfulCubeMapSampling() const
{
if (mState.getClientMajorVersion() != 2)
{
return false;
}
if (mRenderer->getFeatures().disallowSeamfulCubeMapEmulation.enabled)
{
return false;
}
constexpr VkSubgroupFeatureFlags kSeamfulCubeMapSubgroupOperations =
VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT |
VK_SUBGROUP_FEATURE_QUAD_BIT;
const VkSubgroupFeatureFlags deviceSupportedOperations =
mRenderer->getPhysicalDeviceSubgroupProperties().supportedOperations;
bool hasSeamfulCubeMapSubgroupOperations =
(deviceSupportedOperations & kSeamfulCubeMapSubgroupOperations) ==
kSeamfulCubeMapSubgroupOperations;
// Only enable seamful cube map emulation if the necessary subgroup operations are supported.
// Without them, we cannot remove derivative-related artifacts caused by helper invocations.
return hasSeamfulCubeMapSubgroupOperations;
}
} // namespace rx
......@@ -313,6 +313,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
void updateScissor(const gl::State &glState);
bool emulateSeamfulCubeMapSampling() const { return mEmulateSeamfulCubeMapSampling; }
private:
// Dirty bits.
enum DirtyBitType : size_t
......@@ -470,6 +472,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
void waitForSwapchainImageIfNecessary();
bool shouldEmulateSeamfulCubeMapSampling() const;
vk::PipelineHelper *mCurrentGraphicsPipeline;
vk::PipelineAndSerial *mCurrentComputePipeline;
gl::PrimitiveMode mCurrentDrawMode;
......@@ -531,6 +535,9 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
// at the end of the command buffer to make that write available to the host.
bool mIsAnyHostVisibleBufferWritten;
// Whether this context should do seamful cube map sampling emulation.
bool mEmulateSeamfulCubeMapSampling;
struct DriverUniformsDescriptorSet
{
vk::DynamicBuffer dynamicBuffer;
......
......@@ -28,6 +28,7 @@ ANGLE_REENABLE_EXTRA_SEMI_WARNING
#include "common/utilities.h"
#include "libANGLE/Caps.h"
#include "libANGLE/ProgramLinkedResources.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
namespace rx
......@@ -935,6 +936,7 @@ void GlslangWrapper::GetShaderSource(const gl::ProgramState &programState,
angle::Result GlslangWrapper::GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
bool enableSeamfulCubeMapEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
......@@ -954,17 +956,20 @@ angle::Result GlslangWrapper::GetShaderCode(vk::Context *context,
kVersionDefine, kLineRasterDefine),
VK_ERROR_INVALID_SHADER_NV);
return GetShaderCodeImpl(context, glCaps, patchedSources, shaderCodeOut);
return GetShaderCodeImpl(context, glCaps, enableSeamfulCubeMapEmulation, patchedSources,
shaderCodeOut);
}
else
{
return GetShaderCodeImpl(context, glCaps, shaderSources, shaderCodeOut);
return GetShaderCodeImpl(context, glCaps, enableSeamfulCubeMapEmulation, shaderSources,
shaderCodeOut);
}
}
// static
angle::Result GlslangWrapper::GetShaderCodeImpl(vk::Context *context,
const gl::Caps &glCaps,
bool enableSeamfulCubeMapEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
......@@ -1000,6 +1005,11 @@ angle::Result GlslangWrapper::GetShaderCodeImpl(vk::Context *context,
glslang::TShader *shader = shaders[shaderType];
shader->setStringsWithLengths(&shaderString, &shaderLength, 1);
shader->setEntryPoint("main");
if (enableSeamfulCubeMapEmulation)
{
// Enable SPIR-V 1.3 if this workaround is used, as it uses subgroup operations.
shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
}
bool result = shader->parse(&builtInResources, 450, ECoreProfile, false, false, messages);
if (!result)
......
......@@ -29,12 +29,14 @@ class GlslangWrapper
static angle::Result GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
bool enableSeamfulCubeMapEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
private:
static angle::Result GetShaderCodeImpl(vk::Context *context,
const gl::Caps &glCaps,
bool enableSeamfulCubeMapEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
};
......
......@@ -306,7 +306,8 @@ angle::Result ProgramVk::ShaderInfo::initShaders(ContextVk *contextVk,
gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
ANGLE_TRY(GlslangWrapper::GetShaderCode(
contextVk, contextVk->getCaps(), enableLineRasterEmulation, shaderSources, &shaderCodes));
contextVk, contextVk->getCaps(), enableLineRasterEmulation,
contextVk->emulateSeamfulCubeMapSampling(), shaderSources, &shaderCodes));
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
......@@ -1436,6 +1437,8 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
const gl::ActiveTextureArray<TextureVk *> &activeTextures = contextVk->getActiveTextures();
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
++textureIndex)
{
......@@ -1457,6 +1460,13 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
imageInfo.imageView = textureVk->getReadImageView().getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
if (emulateSeamfulCubeMapSampling)
{
// If emulating seamful cubemapping, use the fetch image view. This is basically
// the same image view as read, except it's a 2DArray view for cube maps.
imageInfo.imageView = textureVk->getFetchImageView().getHandle();
}
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
......
......@@ -490,6 +490,7 @@ RendererVk::RendererVk()
mDebugUtilsMessenger(VK_NULL_HANDLE),
mDebugReportCallback(VK_NULL_HANDLE),
mPhysicalDevice(VK_NULL_HANDLE),
mPhysicalDeviceSubgroupProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES},
mQueue(VK_NULL_HANDLE),
mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
mMaxVertexAttribDivisor(1),
......@@ -1005,6 +1006,15 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
createInfo.pEnabledFeatures = &enabledFeatures.features;
}
if (vkGetPhysicalDeviceProperties2KHR)
{
VkPhysicalDeviceProperties2 deviceProperties = {};
deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
deviceProperties.pNext = &mPhysicalDeviceSubgroupProperties;
vkGetPhysicalDeviceProperties2KHR(mPhysicalDevice, &deviceProperties);
}
createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size());
createInfo.ppEnabledExtensionNames =
enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data();
......@@ -1264,6 +1274,11 @@ void RendererVk::initFeatures(const ExtensionNameList &deviceExtensionNames)
mFeatures.perFrameWindowSizeQuery.enabled = true;
}
if (IsWindows() && IsAMD(mPhysicalDeviceProperties.vendorID))
{
mFeatures.disallowSeamfulCubeMapEmulation.enabled = true;
}
if (IsAndroid() && IsQualcomm(mPhysicalDeviceProperties.vendorID))
{
mFeatures.forceD16TexFilter.enabled = true;
......
......@@ -72,6 +72,10 @@ class RendererVk : angle::NonCopyable
{
return mPhysicalDeviceProperties;
}
const VkPhysicalDeviceSubgroupProperties &getPhysicalDeviceSubgroupProperties() const
{
return mPhysicalDeviceSubgroupProperties;
}
const VkPhysicalDeviceFeatures &getPhysicalDeviceFeatures() const
{
return mPhysicalDeviceFeatures;
......@@ -190,6 +194,7 @@ class RendererVk : angle::NonCopyable
VkDebugReportCallbackEXT mDebugReportCallback;
VkPhysicalDevice mPhysicalDevice;
VkPhysicalDeviceProperties mPhysicalDeviceProperties;
VkPhysicalDeviceSubgroupProperties mPhysicalDeviceSubgroupProperties;
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures;
std::vector<VkQueueFamilyProperties> mQueueFamilyProperties;
std::mutex mQueueMutex;
......
......@@ -40,6 +40,11 @@ std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *conte
compileOptions |= SH_CLAMP_POINT_SIZE;
}
if (contextVk->emulateSeamfulCubeMapSampling())
{
compileOptions |= SH_EMULATE_SEAMFUL_CUBE_MAP_SAMPLING;
}
return compileImpl(context, compilerInstance, mData.getSource(), compileOptions | options);
}
......
......@@ -153,7 +153,8 @@ class TextureVk : public TextureImpl
void releaseOwnershipOfImage(const gl::Context *context);
const vk::ImageView &getReadImageView() const;
// A special view for cube maps as a 2D array, used with shaders that do texelFetch().
// A special view for cube maps as a 2D array, used with shaders that do texelFetch() and for
// seamful cube map emulation.
const vk::ImageView &getFetchImageView() const;
angle::Result getLayerLevelDrawImageView(vk::Context *context,
size_t layer,
......
......@@ -280,9 +280,6 @@
3309 NEXUS5X GLES : dEQP-GLES2.functional.uniform_api.random.3 = FAIL
3309 NEXUS5X GLES : dEQP-GLES2.functional.uniform_api.random.54 = FAIL
// General Vulkan failures
3300 VULKAN : dEQP-GLES2.functional.shaders.texture_functions.vertex.texturecubelod = FAIL
// Only seen failing on Android
3241 VULKAN ANDROID : dEQP-GLES2.functional.depth_stencil_clear.depth_scissored_masked = FAIL
......@@ -290,13 +287,6 @@
3253 VULKAN : dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_center = FAIL
3253 VULKAN : dEQP-GLES2.functional.clipping.point.wide_point_clip_viewport_corner = FAIL
// These seem to fail on both D3D11 and Vulkan
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_nearest = FAIL
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_linear = FAIL
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_nearest = FAIL
3243 VULKAN : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_linear = FAIL
// D3D11 AMD already covered by Line 148
3243 D3D11 INTEL : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL
3243 D3D11 INTEL : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL
......@@ -312,16 +302,6 @@
3243 D3D11 NVIDIA : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_linear = FAIL
// Fail with very tiny pixel differences
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_clamp = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_mirror = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_clamp = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_mirror = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_clamp = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_repeat = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_mirror = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_clamp = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_repeat = FAIL
3240 VULKAN : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_mirror = FAIL
3240 D3D11 : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_clamp = FAIL
3240 D3D11 : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_mirror = FAIL
3240 D3D11 : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_clamp = FAIL
......@@ -349,6 +329,44 @@
3306 VULKAN ANDROID : dEQP-GLES2.functional.polygon_offset.fixed16_factor_1_slope = FAIL
3307 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.projected.nearest_linear = FAIL
// Seamful cubemap sampling failures on Android (due to missing support subgroupQuad* operations).
3243 VULKAN ANDROID : dEQP-GLES2.functional.shaders.texture_functions.vertex.texturecubelod = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_nearest = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_linear = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_nearest = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_linear = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_clamp = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_mirror = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_clamp = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_mirror = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_clamp = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_repeat = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_mirror = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_clamp = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_repeat = FAIL
3243 VULKAN ANDROID : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_mirror = FAIL
// These tests also fail on AMD windows driver as it is not allowed to use emulation due to errors.
3243 VULKAN WIN AMD : dEQP-GLES2.functional.shaders.texture_functions.vertex.texturecubelod = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_nearest = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.projected.linear_linear = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_nearest = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.mipmap.cube.bias.linear_linear = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_clamp = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_nearest_mirror = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_clamp = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.filtering.linear_mipmap_linear_linear_mirror = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_clamp = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_repeat = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.clamp_mirror = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_clamp = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_repeat = FAIL
3243 VULKAN WIN AMD : dEQP-GLES2.functional.texture.vertex.cube.wrap.mirror_mirror = FAIL
// Vulkan AMD drivers don't seem to support wide point clipping.
2463 VULKAN WIN AMD : dEQP-GLES2.functional.clipping.point.wide_point_clip = FAIL
......
......@@ -5,6 +5,7 @@
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
......@@ -112,6 +113,154 @@ TEST_P(CubeMapTextureTest, RenderToFacesConsecutively)
EXPECT_GL_NO_ERROR();
}
// Verify that cube map sampling follows the rules that map cubemap coordinates to coordinates
// within each face. See section 3.7.5 of GLES2.0 (Cube Map Texture Selection).
TEST_P(CubeMapTextureTest, SampleCoordinateTransform)
{
// Fails to compile the shader. anglebug.com/3776
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
constexpr GLsizei kCubeFaceCount = 6;
constexpr GLsizei kCubeFaceSectionCount = 4;
constexpr GLsizei kCubeFaceSectionCountSqrt = 2;
constexpr GLColor faceColors[kCubeFaceCount][kCubeFaceSectionCount] = {
{GLColor(255, 0, 0, 255), GLColor(191, 0, 0, 255), GLColor(127, 0, 0, 255),
GLColor(63, 0, 0, 255)},
{GLColor(0, 255, 0, 255), GLColor(0, 191, 0, 255), GLColor(0, 127, 0, 255),
GLColor(0, 63, 0, 255)},
{GLColor(0, 0, 255, 255), GLColor(0, 0, 191, 255), GLColor(0, 0, 127, 255),
GLColor(0, 0, 63, 255)},
{GLColor(255, 63, 0, 255), GLColor(191, 127, 0, 255), GLColor(127, 191, 0, 255),
GLColor(63, 255, 0, 255)},
{GLColor(0, 255, 63, 255), GLColor(0, 191, 127, 255), GLColor(0, 127, 191, 255),
GLColor(0, 63, 255, 255)},
{GLColor(63, 0, 255, 255), GLColor(127, 0, 191, 255), GLColor(191, 0, 127, 255),
GLColor(255, 0, 63, 255)},
};
constexpr GLsizei kTextureSize = 32;
GLTexture tex;
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
for (GLenum face = 0; face < kCubeFaceCount; face++)
{
std::vector<GLColor> faceData(kTextureSize * kTextureSize);
// Create the face with four sections, each with a solid color from |faceColors|.
for (size_t row = 0; row < kTextureSize / kCubeFaceSectionCountSqrt; ++row)
{
for (size_t col = 0; col < kTextureSize / kCubeFaceSectionCountSqrt; ++col)
{
for (size_t srow = 0; srow < kCubeFaceSectionCountSqrt; ++srow)
{
for (size_t scol = 0; scol < kCubeFaceSectionCountSqrt; ++scol)
{
size_t r = row + srow * kTextureSize / kCubeFaceSectionCountSqrt;
size_t c = col + scol * kTextureSize / kCubeFaceSectionCountSqrt;
size_t s = srow * kCubeFaceSectionCountSqrt + scol;
faceData[r * kTextureSize + c] = faceColors[face][s];
}
}
}
}
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, kTextureSize, kTextureSize,
0, GL_RGBA, GL_UNSIGNED_BYTE, faceData.data());
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
EXPECT_GL_NO_ERROR();
GLTexture fboTex;
glBindTexture(GL_TEXTURE_2D, fboTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kCubeFaceCount, kCubeFaceSectionCount, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
EXPECT_GL_NO_ERROR();
// Create a program that samples from 6x4 directions of the cubemap, draw and verify that the
// colors match the right color from |faceColors|.
constexpr char kFS[] = R"(precision mediump float;
uniform samplerCube texCube;
const mat4 coordInSection = mat4(
vec4(-0.5, -0.5, 0, 0),
vec4( 0.5, -0.5, 0, 0),
vec4(-0.5, 0.5, 0, 0),
vec4( 0.5, 0.5, 0, 0)
);
void main()
{
vec3 coord;
if (gl_FragCoord.x < 2.0)
{
coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0;
coord.zy = coordInSection[int(gl_FragCoord.y)].xy;
}
else if (gl_FragCoord.x < 4.0)
{
coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0;
coord.xz = coordInSection[int(gl_FragCoord.y)].xy;
}
else
{
coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0;
coord.xy = coordInSection[int(gl_FragCoord.y)].xy;
}
gl_FragColor = textureCube(texCube, coord);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint texCubeLocation = glGetUniformLocation(program, "texCube");
ASSERT_NE(-1, texCubeLocation);
glUniform1i(texCubeLocation, 0);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
for (GLenum face = 0; face < kCubeFaceCount; face++)
{
// The following table defines the translation from textureCube coordinates to coordinates
// in each face. The framebuffer has width 6 and height 4. Every column corresponding to
// an x value represents one cube face. The values in rows are samples from the four
// sections of the face.
//
// Major Axis Direction Target sc tc ma
// +rx TEXTURE_CUBE_MAP_POSITIVE_X −rz −ry rx
// −rx TEXTURE_CUBE_MAP_NEGATIVE_X rz −ry rx
// +ry TEXTURE_CUBE_MAP_POSITIVE_Y rx rz ry
// −ry TEXTURE_CUBE_MAP_NEGATIVE_Y rx −rz ry
// +rz TEXTURE_CUBE_MAP_POSITIVE_Z rx −ry rz
// −rz TEXTURE_CUBE_MAP_NEGATIVE_Z −rx −ry rz
//
// This table is used only to determine the direction of growth for s and t. The shader
// always generates (row,col) coordinates (0, 0), (0, 1), (1, 0), (1, 1) which is the order
// the data is uploaded to the faces, but based on the table above, the sample order would
// be different.
constexpr size_t faceSampledSections[kCubeFaceCount][kCubeFaceSectionCount] = {
{3, 2, 1, 0}, {2, 3, 0, 1}, {0, 1, 2, 3}, {2, 3, 0, 1}, {2, 3, 0, 1}, {3, 2, 1, 0},
};
for (size_t section = 0; section < kCubeFaceSectionCount; ++section)
{
const GLColor sectionColor = faceColors[face][faceSampledSections[face][section]];
EXPECT_PIXEL_COLOR_EQ(face, section, sectionColor)
<< "face " << face << ", section " << section;
}
}
EXPECT_GL_NO_ERROR();
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(CubeMapTextureTest,
......@@ -120,4 +269,5 @@ ANGLE_INSTANTIATE_TEST(CubeMapTextureTest,
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES(),
ES2_VULKAN());
ES2_VULKAN(),
ES3_VULKAN());
......@@ -176,18 +176,10 @@ bool ShouldAlwaysForceNewDisplay()
}
} // anonymous namespace
GLColorRGB::GLColorRGB() : R(0), G(0), B(0) {}
GLColorRGB::GLColorRGB(GLubyte r, GLubyte g, GLubyte b) : R(r), G(g), B(b) {}
GLColorRGB::GLColorRGB(const Vector3 &floatColor)
: R(ColorDenorm(floatColor.x())), G(ColorDenorm(floatColor.y())), B(ColorDenorm(floatColor.z()))
{}
GLColor::GLColor() : R(0), G(0), B(0), A(0) {}
GLColor::GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a) : R(r), G(g), B(b), A(a) {}
GLColor::GLColor(const Vector4 &floatColor)
: R(ColorDenorm(floatColor.x())),
G(ColorDenorm(floatColor.y())),
......
......@@ -76,8 +76,8 @@ namespace angle
{
struct GLColorRGB
{
GLColorRGB();
GLColorRGB(GLubyte r, GLubyte g, GLubyte b);
constexpr GLColorRGB() : R(0), G(0), B(0) {}
constexpr GLColorRGB(GLubyte r, GLubyte g, GLubyte b) : R(r), G(g), B(b) {}
GLColorRGB(const angle::Vector3 &floatColor);
const GLubyte *data() const { return &R; }
......@@ -94,8 +94,8 @@ struct GLColorRGB
struct GLColor
{
GLColor();
GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a);
constexpr GLColor() : R(0), G(0), B(0), A(0) {}
constexpr GLColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a) : R(r), G(g), B(b), A(a) {}
GLColor(const angle::Vector4 &floatColor);
GLColor(GLuint colorValue);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment