[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