Support depth-only/stencil-only COPY_SRC on OpenGL

Bug: dawn:439
Change-Id: I09d33d3115d54c03e3ba5a32f34843065edb8020
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24961
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index c88aef4..be6b58d 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -445,7 +445,7 @@
         : CommandBufferBase(encoder, descriptor) {
     }
 
-    void CommandBuffer::Execute() {
+    MaybeError CommandBuffer::Execute() {
         const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
 
         auto TransitionForPass = [](const PassResourceUsage& usages) {
@@ -518,6 +518,12 @@
                     GLenum target = texture->GetGLTarget();
                     const GLFormat& format = texture->GetGLFormat();
 
+                    if (dst.aspect == Aspect::Stencil) {
+                        return DAWN_VALIDATION_ERROR(
+                            "Copies to stencil textures unsupported on OpenGL");
+                    }
+                    ASSERT(dst.aspect == Aspect::Color);
+
                     buffer->EnsureDataInitialized();
 
                     ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
@@ -601,13 +607,13 @@
                     auto& copySize = copy->copySize;
                     Texture* texture = ToBackend(src.texture.Get());
                     Buffer* buffer = ToBackend(dst.buffer.Get());
-                    const Format& format = texture->GetFormat();
-                    const GLFormat& glFormat = texture->GetGLFormat();
+                    const Format& formatInfo = texture->GetFormat();
+                    const GLFormat& format = texture->GetGLFormat();
                     GLenum target = texture->GetGLTarget();
 
                     // TODO(jiawei.shao@intel.com): support texture-to-buffer copy with compressed
                     // texture formats.
-                    if (format.isCompressed) {
+                    if (formatInfo.isCompressed) {
                         UNREACHABLE();
                     }
 
@@ -625,22 +631,35 @@
                     gl.GenFramebuffers(1, &readFBO);
                     gl.BindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
 
-                    GLenum glAttachment = 0;
-                    if (format.aspects == (Aspect::Depth | Aspect::Stencil)) {
-                        glAttachment = GL_DEPTH_STENCIL_ATTACHMENT;
-                    } else if (format.aspects == Aspect::Depth) {
-                        glAttachment = GL_DEPTH_ATTACHMENT;
-                    } else if (format.aspects == Aspect::Stencil) {
-                        glAttachment = GL_STENCIL_ATTACHMENT;
-                    } else if (format.aspects == Aspect::Color) {
-                        glAttachment = GL_COLOR_ATTACHMENT0;
-                    } else {
-                        UNREACHABLE();
-                    }
+                    const TexelBlockInfo& blockInfo = formatInfo.GetTexelBlockInfo(src.aspect);
 
                     gl.BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle());
-                    gl.PixelStorei(GL_PACK_ROW_LENGTH, dst.bytesPerRow / format.blockByteSize);
                     gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, dst.rowsPerImage);
+                    gl.PixelStorei(GL_PACK_ROW_LENGTH, dst.bytesPerRow / blockInfo.blockByteSize);
+
+                    GLenum glAttachment;
+                    GLenum glFormat;
+                    GLenum glType;
+                    switch (src.aspect) {
+                        case Aspect::Color:
+                            glAttachment = GL_COLOR_ATTACHMENT0;
+                            glFormat = format.format;
+                            glType = format.type;
+                            break;
+                        case Aspect::Depth:
+                            glAttachment = GL_DEPTH_ATTACHMENT;
+                            glFormat = GL_DEPTH_COMPONENT;
+                            glType = GL_FLOAT;
+                            break;
+                        case Aspect::Stencil:
+                            glAttachment = GL_STENCIL_ATTACHMENT;
+                            glFormat = GL_STENCIL_INDEX;
+                            glType = GL_UNSIGNED_BYTE;
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
+                    }
 
                     uint8_t* offset =
                         reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(dst.offset));
@@ -650,8 +669,7 @@
                                 gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment, target,
                                                         texture->GetHandle(), src.mipLevel);
                                 gl.ReadPixels(src.origin.x, src.origin.y, copySize.width,
-                                              copySize.height, glFormat.format, glFormat.type,
-                                              offset);
+                                              copySize.height, glFormat, glType, offset);
                                 break;
                             }
 
@@ -661,8 +679,7 @@
                                                            texture->GetHandle(), src.mipLevel,
                                                            src.origin.z + layer);
                                 gl.ReadPixels(src.origin.x, src.origin.y, copySize.width,
-                                              copySize.height, glFormat.format, glFormat.type,
-                                              offset);
+                                              copySize.height, glFormat, glType, offset);
 
                                 offset += bytesPerImage;
                             }
@@ -731,6 +748,8 @@
                 }
             }
         }
+
+        return {};
     }
 
     void CommandBuffer::ExecuteComputePass() {
diff --git a/src/dawn_native/opengl/CommandBufferGL.h b/src/dawn_native/opengl/CommandBufferGL.h
index eceb533..b860be8 100644
--- a/src/dawn_native/opengl/CommandBufferGL.h
+++ b/src/dawn_native/opengl/CommandBufferGL.h
@@ -29,7 +29,7 @@
       public:
         CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
 
-        void Execute();
+        MaybeError Execute();
 
       private:
         void ExecuteComputePass();
diff --git a/src/dawn_native/opengl/QueueGL.cpp b/src/dawn_native/opengl/QueueGL.cpp
index 88d1575..340ec25 100644
--- a/src/dawn_native/opengl/QueueGL.cpp
+++ b/src/dawn_native/opengl/QueueGL.cpp
@@ -30,7 +30,7 @@
 
         TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
         for (uint32_t i = 0; i < commandCount; ++i) {
-            ToBackend(commands[i])->Execute();
+            DAWN_TRY(ToBackend(commands[i])->Execute());
         }
         TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute");
 
diff --git a/src/tests/end2end/DepthStencilCopyTests.cpp b/src/tests/end2end/DepthStencilCopyTests.cpp
index 22e3e63..55cb721 100644
--- a/src/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/tests/end2end/DepthStencilCopyTests.cpp
@@ -153,6 +153,9 @@
 
 // Test copying to the stencil-aspect of a buffer
 TEST_P(DepthStencilCopyTests, ToStencilAspect) {
+    // Copies to a single aspect are unsupported on OpenGL.
+    DAWN_SKIP_TEST_IF(IsOpenGL());
+
     // TODO(enga): Figure out why this fails on Vulkan Intel
     // Results are shifted by 1 byte on Windows, and crash/hang on Linux.
     DAWN_SKIP_TEST_IF(IsVulkan() && IsIntel());
@@ -320,4 +323,8 @@
     }
 }
 
-DAWN_INSTANTIATE_TEST(DepthStencilCopyTests, D3D12Backend(), MetalBackend(), VulkanBackend());
+DAWN_INSTANTIATE_TEST(DepthStencilCopyTests,
+                      D3D12Backend(),
+                      MetalBackend(),
+                      OpenGLBackend(),
+                      VulkanBackend());