[Compat] Fix Cube/CubeArray texture and view binding
Remove copy path for Cube/CubeArray textures. Add missing
textureViewDimension support and various GL calls for
GL_TEXTURE_CUBE_MAP(_ARRAY). Add related tests.
Also fixes blit texture to buffer workaround for stencil
cube texture where textureSampleLevel doesn't support
texture_cube<u32>, use textureGather instead.
This fix the Cube textureDimension CTS failure along
the way.
Bug: dawn:2131, dawn:2442, dawn:2182
Change-Id: I87229d7a0f43f9496738eeee69a73c2a8c8ab81b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/184840
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/BlitTextureToBuffer.cpp b/src/dawn/native/BlitTextureToBuffer.cpp
index 9a3e3e0..3af39de 100644
--- a/src/dawn/native/BlitTextureToBuffer.cpp
+++ b/src/dawn/native/BlitTextureToBuffer.cpp
@@ -133,11 +133,15 @@
@group(0) @binding(0) var src_tex : texture_2d_array<u32>;
)";
+// textureSampleLevel doesn't support texture_cube<u32>
+// Use textureGather as a workaround.
+// Always choose the texel with the smallest coord (stored in w component).
+// Since this is only used for Stencil8 (1 channel), we only care component idx == 0.
constexpr std::string_view kUintTextureCube = R"(
@group(1) @binding(0) var default_sampler: sampler;
fn textureLoadGeneral(tex: texture_cube<u32>, coords: vec3u, level: u32) -> vec4<u32> {
let sample_coords = coordToCubeSampleST(coords, params.levelSize);
- return textureSampleLevel(tex, default_sampler, sample_coords, f32(level));
+ return vec4<u32>(textureGather(0, tex, default_sampler, sample_coords).w);
}
@group(0) @binding(0) var src_tex : texture_cube<u32>;
)";
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index 057d36d..a004327 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -69,10 +69,6 @@
DAWN_UNREACHABLE();
}
-bool Is1DOr2D(wgpu::TextureDimension dimension) {
- return dimension == wgpu::TextureDimension::e1D || dimension == wgpu::TextureDimension::e2D;
-}
-
GLenum VertexFormatType(wgpu::VertexFormat format) {
switch (format) {
case wgpu::VertexFormat::Uint8x2:
@@ -738,7 +734,7 @@
copySize.height, glFormat, glType, offset);
break;
}
- // Implementation for 2D array is the same as 3D.
+ // Implementation for 2D array and cube map is the same as 3D.
[[fallthrough]];
}
@@ -1441,7 +1437,7 @@
gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT, blockInfo.height);
gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, 1);
- if (texture->GetArrayLayers() == 1 && Is1DOr2D(texture->GetDimension())) {
+ if (target == GL_TEXTURE_2D) {
gl.CompressedTexSubImage2D(target, destination.mipLevel, x, y, width, height,
format.internalFormat, imageSize, data);
} else {
@@ -1458,7 +1454,7 @@
gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT, 0);
gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, 0);
} else {
- if (texture->GetArrayLayers() == 1 && Is1DOr2D(texture->GetDimension())) {
+ if (target == GL_TEXTURE_2D) {
const uint8_t* d = static_cast<const uint8_t*>(data);
for (; y < destination.origin.y + copySize.height; y += blockInfo.height) {
@@ -1499,10 +1495,22 @@
gl.PixelStorei(GL_UNPACK_ALIGNMENT, std::min(8u, blockInfo.byteSize));
gl.PixelStorei(GL_UNPACK_ROW_LENGTH,
dataLayout.bytesPerRow / blockInfo.byteSize * blockInfo.width);
- if (texture->GetArrayLayers() == 1 && Is1DOr2D(texture->GetDimension())) {
+ if (target == GL_TEXTURE_2D) {
gl.TexSubImage2D(target, destination.mipLevel, x, y, width, height, adjustedFormat,
format.type, data);
+ } else if (target == GL_TEXTURE_CUBE_MAP) {
+ DAWN_ASSERT(texture->GetArrayLayers() == 6);
+ const uint8_t* pointer = static_cast<const uint8_t*>(data);
+ uint32_t baseLayer = destination.origin.z;
+ for (uint32_t l = 0; l < copySize.depthOrArrayLayers; ++l) {
+ GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + baseLayer + l;
+ gl.TexSubImage2D(cubeMapTarget, destination.mipLevel, x, y, width, height,
+ adjustedFormat, format.type, pointer);
+ pointer += dataLayout.rowsPerImage * dataLayout.bytesPerRow;
+ }
} else {
+ DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
+ target == GL_TEXTURE_CUBE_MAP_ARRAY);
gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, dataLayout.rowsPerImage * blockInfo.height);
gl.TexSubImage3D(target, destination.mipLevel, x, y, z, width, height,
copySize.depthOrArrayLayers, adjustedFormat, format.type, data);
@@ -1511,14 +1519,30 @@
gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
gl.PixelStorei(GL_UNPACK_ALIGNMENT, 4); // Reset to default
} else {
- if (texture->GetArrayLayers() == 1 && Is1DOr2D(texture->GetDimension())) {
+ if (target == GL_TEXTURE_2D) {
const uint8_t* d = static_cast<const uint8_t*>(data);
for (; y < destination.origin.y + height; ++y) {
gl.TexSubImage2D(target, destination.mipLevel, x, y, width, 1, adjustedFormat,
format.type, d);
d += dataLayout.bytesPerRow;
}
+ } else if (target == GL_TEXTURE_CUBE_MAP) {
+ DAWN_ASSERT(texture->GetArrayLayers() == 6);
+ const uint8_t* pointer = static_cast<const uint8_t*>(data);
+ uint32_t baseLayer = destination.origin.z;
+ for (uint32_t l = 0; l < copySize.depthOrArrayLayers; ++l) {
+ const uint8_t* d = pointer;
+ GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + baseLayer + l;
+ for (y = destination.origin.y; y < destination.origin.y + height; ++y) {
+ gl.TexSubImage2D(cubeMapTarget, destination.mipLevel, x, y, width, 1,
+ adjustedFormat, format.type, d);
+ d += dataLayout.bytesPerRow;
+ }
+ pointer += dataLayout.rowsPerImage * dataLayout.bytesPerRow;
+ }
} else {
+ DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
+ target == GL_TEXTURE_CUBE_MAP_ARRAY);
const uint8_t* slice = static_cast<const uint8_t*>(data);
for (; z < destination.origin.z + copySize.depthOrArrayLayers; ++z) {
const uint8_t* d = slice;
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp
index fd53496..2f5518a 100644
--- a/src/dawn/native/opengl/TextureGL.cpp
+++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -44,58 +44,31 @@
namespace {
-GLenum TargetForTexture(const UnpackedPtr<TextureDescriptor>& descriptor) {
- switch (descriptor->dimension) {
- case wgpu::TextureDimension::Undefined:
- DAWN_UNREACHABLE();
- case wgpu::TextureDimension::e1D:
- case wgpu::TextureDimension::e2D:
- if (descriptor->size.depthOrArrayLayers > 1) {
- DAWN_ASSERT(descriptor->sampleCount == 1);
- return GL_TEXTURE_2D_ARRAY;
- } else {
- if (descriptor->sampleCount > 1) {
- return GL_TEXTURE_2D_MULTISAMPLE;
- } else {
- return GL_TEXTURE_2D;
- }
- }
- case wgpu::TextureDimension::e3D:
- DAWN_ASSERT(descriptor->sampleCount == 1);
- return GL_TEXTURE_3D;
- }
- DAWN_UNREACHABLE();
-}
-
-GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension,
- uint32_t arrayLayerCount,
- uint32_t sampleCount) {
+GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension, uint32_t sampleCount) {
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::e2D:
return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
case wgpu::TextureViewDimension::e2DArray:
if (sampleCount > 1) {
- DAWN_ASSERT(arrayLayerCount == 1);
return GL_TEXTURE_2D_MULTISAMPLE;
}
DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_2D_ARRAY;
case wgpu::TextureViewDimension::Cube:
DAWN_ASSERT(sampleCount == 1);
- DAWN_ASSERT(arrayLayerCount == 6);
return GL_TEXTURE_CUBE_MAP;
case wgpu::TextureViewDimension::CubeArray:
DAWN_ASSERT(sampleCount == 1);
- DAWN_ASSERT(arrayLayerCount % 6 == 0);
return GL_TEXTURE_CUBE_MAP_ARRAY;
case wgpu::TextureViewDimension::e3D:
+ DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_3D;
case wgpu::TextureViewDimension::Undefined:
- break;
+ default:
+ DAWN_UNREACHABLE();
}
- DAWN_UNREACHABLE();
}
bool RequiresCreatingNewTextureView(
@@ -141,16 +114,6 @@
return true;
}
- // TODO(dawn:2131): remove once compatibility texture binding view dimension is fully
- // implemented.
- switch (textureViewDescriptor->dimension) {
- case wgpu::TextureViewDimension::Cube:
- case wgpu::TextureViewDimension::CubeArray:
- return true;
- default:
- break;
- }
-
return false;
}
@@ -165,6 +128,7 @@
// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTextureView.xhtml
switch (target) {
case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
case GL_TEXTURE_3D:
gl.TexStorage3D(target, levels, internalFormat, size.width, size.height,
size.depthOrArrayLayers);
@@ -226,7 +190,8 @@
Texture::Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor, GLuint handle)
: TextureBase(device, descriptor), mHandle(handle) {
- mTarget = TargetForTexture(descriptor);
+ mTarget = TargetForTextureViewDimension(GetCompatibilityTextureBindingViewDimension(),
+ descriptor->sampleCount);
}
Texture::~Texture() {}
@@ -570,8 +535,7 @@
TextureView::TextureView(TextureBase* texture, const UnpackedPtr<TextureViewDescriptor>& descriptor)
: TextureViewBase(texture, descriptor), mOwnsHandle(false) {
- mTarget = TargetForTextureViewDimension(descriptor->dimension, descriptor->arrayLayerCount,
- texture->GetSampleCount());
+ mTarget = TargetForTextureViewDimension(descriptor->dimension, texture->GetSampleCount());
// Texture could be destroyed by the time we make a view.
if (GetTexture()->IsDestroyed()) {
@@ -648,10 +612,21 @@
}
DAWN_ASSERT(handle != 0);
- if (textarget == GL_TEXTURE_2D_ARRAY || textarget == GL_TEXTURE_3D) {
- gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, arrayLayer);
- } else {
- gl.FramebufferTexture2D(target, attachment, textarget, handle, mipLevel);
+
+ switch (textarget) {
+ case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ case GL_TEXTURE_3D:
+ gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, arrayLayer);
+ break;
+ case GL_TEXTURE_CUBE_MAP: {
+ GLenum cubeTexTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + arrayLayer;
+ gl.FramebufferTexture2D(target, attachment, cubeTexTarget, handle, mipLevel);
+ break;
+ }
+ default:
+ gl.FramebufferTexture2D(target, attachment, textarget, handle, mipLevel);
+ break;
}
}
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index 6d8b523..c9a595d 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -1722,9 +1722,6 @@
CopyTests_T2B::SetUp();
DAWN_SUPPRESS_TEST_IF(!IsCompatibilityMode());
DAWN_SUPPRESS_TEST_IF(IsANGLESwiftShader());
- // TODO(dawn:2131): remove once fully implemented, so cube texture doesn't require a copy.
- DAWN_SUPPRESS_TEST_IF((IsOpenGL() || IsOpenGLES()) &&
- (GetParam().mTextureFormat == wgpu::TextureFormat::RGB9E5Ufloat));
}
};
diff --git a/src/dawn/tests/end2end/QueueTests.cpp b/src/dawn/tests/end2end/QueueTests.cpp
index 458a71a..42cd2c6 100644
--- a/src/dawn/tests/end2end/QueueTests.cpp
+++ b/src/dawn/tests/end2end/QueueTests.cpp
@@ -300,13 +300,21 @@
void DoTest(const TextureSpec& textureSpec,
const DataSpec& dataSpec,
- const wgpu::Extent3D& copySize) {
+ const wgpu::Extent3D& copySize,
+ const wgpu::TextureViewDimension bindingViewDimension =
+ wgpu::TextureViewDimension::Undefined) {
// Create data of size `size` and populate it
std::vector<uint8_t> data(dataSpec.size);
FillData(data.data(), data.size());
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
wgpu::TextureDescriptor descriptor = {};
+ wgpu::TextureBindingViewDimensionDescriptor textureBindingViewDimensionDesc;
+ if (IsCompatibilityMode() &&
+ bindingViewDimension != wgpu::TextureViewDimension::Undefined) {
+ textureBindingViewDimensionDesc.textureBindingViewDimension = bindingViewDimension;
+ descriptor.nextInChain = &textureBindingViewDimensionDesc;
+ }
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size = textureSpec.textureSize;
descriptor.format = GetParam().mTextureFormat;
@@ -605,6 +613,43 @@
TestBody({1, 2, 0}, {17, 19, 1});
}
+// Test with bytesPerRow greater than needed for cube textures.
+// Made for testing compat behavior.
+TEST_P(QueueWriteTextureTests, VaryingBytesPerRowCube) {
+ // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 4 OpenGLES
+ DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsQualcomm());
+ // TODO(crbug.com/dawn/2295): diagnose this failure on Pixel 6 OpenGLES
+ DAWN_SUPPRESS_TEST_IF(IsOpenGLES() && IsAndroid() && IsARM());
+ // TODO(crbug.com/dawn/2131): diagnose this failure on Win Angle D3D11
+ DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
+
+ constexpr uint32_t kWidth = 257;
+ constexpr uint32_t kHeight = 257;
+
+ TextureSpec textureSpec;
+ textureSpec.textureSize = {kWidth, kHeight, 6};
+ textureSpec.level = 0;
+
+ auto TestBody = [&](wgpu::Origin3D copyOrigin, wgpu::Extent3D copyExtent) {
+ textureSpec.copyOrigin = copyOrigin;
+ for (unsigned int b : {1, 2, 3, 4}) {
+ uint32_t bytesPerRow =
+ copyExtent.width * utils::GetTexelBlockSizeInBytes(GetParam().mTextureFormat) + b;
+ DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow), copyExtent,
+ wgpu::TextureViewDimension::Cube);
+ }
+ };
+
+ TestBody({0, 0, 0}, textureSpec.textureSize);
+
+ if (utils::IsDepthOrStencilFormat(GetParam().mTextureFormat)) {
+ // The entire subresource must be copied when the format is a depth/stencil format.
+ return;
+ }
+
+ TestBody({1, 2, 0}, {17, 17, 1});
+}
+
// Test that writing with bytesPerRow = 0 and bytesPerRow < bytesInACompleteRow works
// when we're copying one row only
TEST_P(QueueWriteTextureTests, BytesPerRowWithOneRowCopy) {
diff --git a/src/dawn/tests/end2end/TextureShaderBuiltinTests.cpp b/src/dawn/tests/end2end/TextureShaderBuiltinTests.cpp
index 847bfa0..a21ac29 100644
--- a/src/dawn/tests/end2end/TextureShaderBuiltinTests.cpp
+++ b/src/dawn/tests/end2end/TextureShaderBuiltinTests.cpp
@@ -207,10 +207,10 @@
TEST_P(TextureShaderBuiltinTests, BaseMipLevelTextureView) {
// TODO(dawn:2538): failing on OpenGLES Angle backed by D3D11.
DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
- constexpr uint32_t kCubeLayers = 1;
+ constexpr uint32_t kLayers = 1;
constexpr uint32_t kMipLevels = 3;
wgpu::Texture tex =
- CreateTexture("tex", kCubeLayers, kMipLevels, 1, wgpu::TextureViewDimension::e2D);
+ CreateTexture("tex", kLayers, kMipLevels, 1, wgpu::TextureViewDimension::e2D);
constexpr uint32_t kBaseMipLevel = 1;
constexpr uint32_t kViewMipLevelCount = 2;
@@ -271,8 +271,9 @@
// Testing that baseMipLevel is handled correctly for texture_cube.
TEST_P(TextureShaderBuiltinTests, BaseMipLevelTextureViewCube) {
- // TODO(dawn:2442): fix texture_cube base mip level bug.
- DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode());
+ // TODO(crbug.com/dawn/2442): diagnose this failure on Win Angle D3D11
+ DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
+
constexpr uint32_t kCubeLayers = 6;
constexpr uint32_t kMipLevels = 3;
wgpu::Texture texCube =
@@ -288,11 +289,8 @@
const uint32_t textureWidthLevel0 = 1 << kMipLevels;
const uint32_t textureWidthLevel1 = textureWidthLevel0 >> 1;
const uint32_t textureWidthLevel2 = textureWidthLevel1 >> 1;
- const uint32_t expected[] = {
- textureWidthLevel1,
- textureWidthLevel1,
- textureWidthLevel2,
- };
+ const uint32_t expected[] = {textureWidthLevel1, textureWidthLevel1, textureWidthLevel2,
+ kViewMipLevelCount};
wgpu::BufferDescriptor bufferDesc;
bufferDesc.size = sizeof(expected);
@@ -309,6 +307,7 @@
dstBuf[0] = textureDimensions(tex_cube).x;
dstBuf[1] = textureDimensions(tex_cube, 0).x;
dstBuf[2] = textureDimensions(tex_cube, 1).x;
+ dstBuf[3] = textureNumLevels(tex_cube);
}
)";
// clang-format on