Fix mipmapped GL_ALPHA UBYTE textures on 9_3
Change-Id: I59020f8152d47091533d69d20fe5ff56e5f96bc1
Reviewed-on: https://chromium-review.googlesource.com/262551
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Austin Kinross <aukinros@microsoft.com>
diff --git a/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp b/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp
index 0697a82..2f81d6d 100644
--- a/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/formatutils11.cpp
@@ -549,7 +549,28 @@
(*map)[internalFormat].push_back(TypeLoadFunctionPair(type, loadFunc));
}
-D3D11LoadFunctionMap BuildD3D11LoadFunctionMap()
+D3D11LoadFunctionMap BuildD3D11_FL9_3_LoadFunctionMap()
+{
+ D3D11LoadFunctionMap map;
+
+ // From GL_EXT_texture_storage. Also used by GL_ALPHA8
+ // On feature level 9_3, A8_UNORM doesn't support mipmaps, so we must use RGBA8 instead
+ InsertLoadFunction(&map, GL_ALPHA8_EXT, GL_UNSIGNED_BYTE, LoadA8ToRGBA8);
+
+ return map;
+}
+
+D3D11LoadFunctionMap BuildD3D11_FL10_0Plus_LoadFunctionMap()
+{
+ D3D11LoadFunctionMap map;
+
+ // From GL_EXT_texture_storage. Also used by GL_ALPHA8
+ InsertLoadFunction(&map, GL_ALPHA8_EXT, GL_UNSIGNED_BYTE, LoadToNative<GLubyte, 1>);
+
+ return map;
+}
+
+D3D11LoadFunctionMap BuildBaseD3D11LoadFunctionMap()
{
D3D11LoadFunctionMap map;
@@ -654,7 +675,7 @@
InsertLoadFunction(&map, GL_ALPHA, GL_HALF_FLOAT_OES, LoadA16FToRGBA16F );
// From GL_EXT_texture_storage
- InsertLoadFunction(&map, GL_ALPHA8_EXT, GL_UNSIGNED_BYTE, LoadToNative<GLubyte, 1> );
+ // GL_ALPHA8_EXT GL_UNSIGNED_BYTE is in the feature-level-specific load function maps, due to differences between 9_3 and 10_0+
InsertLoadFunction(&map, GL_LUMINANCE8_EXT, GL_UNSIGNED_BYTE, LoadL8ToRGBA8 );
InsertLoadFunction(&map, GL_LUMINANCE8_ALPHA8_EXT, GL_UNSIGNED_BYTE, LoadLA8ToRGBA8 );
InsertLoadFunction(&map, GL_ALPHA32F_EXT, GL_FLOAT, LoadA32FToRGBA32F );
@@ -724,8 +745,8 @@
{
}
-static inline void InsertD3D11FormatInfo(D3D11ES3FormatMap *map, GLenum internalFormat, DXGI_FORMAT texFormat,
- DXGI_FORMAT srvFormat, DXGI_FORMAT rtvFormat, DXGI_FORMAT dsvFormat)
+static inline void InsertD3D11FormatInfoBase(D3D11ES3FormatMap *formatMap, const D3D11LoadFunctionMap &flLoadFunctions, GLenum internalFormat, DXGI_FORMAT texFormat,
+ DXGI_FORMAT srvFormat, DXGI_FORMAT rtvFormat, DXGI_FORMAT dsvFormat)
{
TextureFormat info;
info.texFormat = texFormat;
@@ -809,8 +830,8 @@
InternalFormatInitializerMap::const_iterator initializerIter = initializerMap.find(internalFormat);
info.dataInitializerFunction = (initializerIter != initializerMap.end()) ? initializerIter->second : NULL;
- // Gather all the load functions for this internal format
- static const D3D11LoadFunctionMap loadFunctions = BuildD3D11LoadFunctionMap();
+ // Gather all the load functions for this internal format from the base list
+ static const D3D11LoadFunctionMap loadFunctions = BuildBaseD3D11LoadFunctionMap();
D3D11LoadFunctionMap::const_iterator loadFunctionIter = loadFunctions.find(internalFormat);
if (loadFunctionIter != loadFunctions.end())
{
@@ -823,25 +844,55 @@
}
}
- map->insert(std::make_pair(internalFormat, info));
+ // Gather load functions for this internal format from the feature-level-specific list
+ D3D11LoadFunctionMap::const_iterator flLoadFunctionIter = flLoadFunctions.find(internalFormat);
+ if (flLoadFunctionIter != flLoadFunctions.end())
+ {
+ const std::vector<TypeLoadFunctionPair> &flLoadFunctionVector = flLoadFunctionIter->second;
+ for (size_t i = 0; i < flLoadFunctionVector.size(); i++)
+ {
+ GLenum type = flLoadFunctionVector[i].first;
+ LoadImageFunction function = flLoadFunctionVector[i].second;
+ info.loadFunctions.insert(std::make_pair(type, function));
+ }
+ }
+
+ formatMap->insert(std::make_pair(internalFormat, info));
+}
+
+static inline void InsertD3D11_FL9_3_FormatInfo(D3D11ES3FormatMap *map, GLenum internalFormat, DXGI_FORMAT texFormat,
+ DXGI_FORMAT srvFormat, DXGI_FORMAT rtvFormat, DXGI_FORMAT dsvFormat)
+{
+ static const D3D11LoadFunctionMap flLoadFunctions = BuildD3D11_FL9_3_LoadFunctionMap();
+ InsertD3D11FormatInfoBase(map, flLoadFunctions, internalFormat, texFormat, srvFormat, rtvFormat, dsvFormat);
+}
+
+static inline void InsertD3D11FormatInfo(D3D11ES3FormatMap *map, GLenum internalFormat, DXGI_FORMAT texFormat,
+ DXGI_FORMAT srvFormat, DXGI_FORMAT rtvFormat, DXGI_FORMAT dsvFormat)
+{
+ static const D3D11LoadFunctionMap flLoadFunctions = BuildD3D11_FL10_0Plus_LoadFunctionMap();
+ InsertD3D11FormatInfoBase(map, flLoadFunctions, internalFormat, texFormat, srvFormat, rtvFormat, dsvFormat);
}
static D3D11ES3FormatMap BuildD3D11_FL9_3FormatOverrideMap()
{
// D3D11 Feature Level 9_3 doesn't support as many texture formats as Feature Level 10_0+.
// In particular, it doesn't support:
+ // - mipmaps on DXGI_FORMAT_A8_NORM
// - *_TYPELESS formats
// - DXGI_FORMAT_D32_FLOAT_S8X24_UINT or DXGI_FORMAT_D32_FLOAT
D3D11ES3FormatMap map;
- // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format
- InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT16, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM);
- InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT24, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
- InsertD3D11FormatInfo(&map, GL_DEPTH_COMPONENT32F, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
- InsertD3D11FormatInfo(&map, GL_DEPTH24_STENCIL8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
- InsertD3D11FormatInfo(&map, GL_DEPTH32F_STENCIL8, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
- InsertD3D11FormatInfo(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
+ // | GL internal format | D3D11 texture format | D3D11 SRV format | D3D11 RTV format | D3D11 DSV format
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_ALPHA, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_ALPHA8_EXT, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_DEPTH_COMPONENT16, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_DEPTH_COMPONENT24, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_DEPTH_COMPONENT32F, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_DEPTH24_STENCIL8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_DEPTH32F_STENCIL8, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
+ InsertD3D11_FL9_3_FormatInfo(&map, GL_STENCIL_INDEX8, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D24_UNORM_S8_UINT);
return map;
}
diff --git a/src/tests/end2end_tests/TextureTest.cpp b/src/tests/end2end_tests/TextureTest.cpp
index 34ebb79..ef8610d 100644
--- a/src/tests/end2end_tests/TextureTest.cpp
+++ b/src/tests/end2end_tests/TextureTest.cpp
@@ -1,7 +1,7 @@
#include "ANGLETest.h"
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
-ANGLE_TYPED_TEST_CASE(TextureTest, ES2_D3D9, ES2_D3D11);
+ANGLE_TYPED_TEST_CASE(TextureTest, ES2_D3D9, ES2_D3D11, ES2_D3D11_FL9_3);
template<typename T>
class TextureTest : public ANGLETest
@@ -521,3 +521,86 @@
testFloatCopySubImage(4, 4);
}
+
+// Port of https://www.khronos.org/registry/webgl/conformance-suites/1.0.3/conformance/textures/texture-npot.html
+// Run against GL_ALPHA/UNSIGNED_BYTE format, to ensure that D3D11 Feature Level 9_3 correctly handles GL_ALPHA
+TYPED_TEST(TextureTest, TextureNPOT_GL_ALPHA_UBYTE)
+{
+ const int npotTexSize = 5;
+ const int potTexSize = 4; // Should be less than npotTexSize
+ GLuint tex2D;
+
+ if (extensionEnabled("GL_OES_texture_npot"))
+ {
+ // This test isn't applicable if texture_npot is enabled
+ return;
+ }
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ glActiveTexture(GL_TEXTURE0);
+ glGenTextures(1, &tex2D);
+ glBindTexture(GL_TEXTURE_2D, tex2D);
+
+ std::vector<GLubyte> pixels(1 * npotTexSize * npotTexSize);
+ for (size_t pixelId = 0; pixelId < npotTexSize * npotTexSize; ++pixelId)
+ {
+ pixels[pixelId] = 64;
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ // Check that an NPOT texture not on level 0 generates INVALID_VALUE
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+ EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+ // Check that an NPOT texture on level 0 succeeds
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+ EXPECT_GL_NO_ERROR();
+
+ // Check that generateMipmap fails on NPOT
+ glGenerateMipmap(GL_TEXTURE_2D);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ // Check that nothing is drawn if filtering is not correct for NPOT
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glClear(GL_COLOR_BUFFER_BIT);
+ drawQuad(m2DProgram, "position", 1.0f);
+ EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
+
+ // NPOT texture with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+ glClear(GL_COLOR_BUFFER_BIT);
+ drawQuad(m2DProgram, "position", 1.0f);
+ EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
+
+ // NPOT texture with TEXTURE_MIN_FILTER set to LINEAR should draw
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glClear(GL_COLOR_BUFFER_BIT);
+ drawQuad(m2DProgram, "position", 1.0f);
+ EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
+
+ // Check that glTexImage2D for POT texture succeeds
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, potTexSize, potTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+ EXPECT_GL_NO_ERROR();
+
+ // Check that generateMipmap for an POT texture succeeds
+ glGenerateMipmap(GL_TEXTURE_2D);
+ EXPECT_GL_NO_ERROR();
+
+ // POT texture with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR should draw
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glClear(GL_COLOR_BUFFER_BIT);
+ drawQuad(m2DProgram, "position", 1.0f);
+ EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
+ EXPECT_GL_NO_ERROR();
+}