Disable rasterizer discard during CopyTextureCHROMIUM. Otherwise if the user sets this state, the copy command would be silently skipped. It did not seem practical to implement the large-scale refactor suggested in the bug report to avoid setting the texture's level as cleared. Integrated modified version of the test case from the bug report. Co-authored with jetski-cli. Fixed: 500138014 Change-Id: I24c8ddbcfb19e4fadec85bec9f407bde091db8b5 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7798563 Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org> Auto-Submit: Kenneth Russell <kbr@chromium.org> Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org> Cr-Commit-Position: refs/heads/main@{#1621709}
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc index a73cca8..f06fba6a 100644 --- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc +++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -1424,6 +1424,9 @@ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_FALSE); glDisable(GL_BLEND); + if (decoder->GetFeatureInfo()->IsWebGL2OrES3OrHigherContext()) { + glDisable(GL_RASTERIZER_DISCARD); + } bool need_scissor = xoffset || yoffset || width != dest_width || height != dest_height;
diff --git a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc index 0e05828..28135403 100644 --- a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc +++ b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
@@ -1850,4 +1850,102 @@ glDeleteFramebuffers(1, &framebuffer_id_); } +TEST_P(GLCopyTextureCHROMIUMES3Test, RasterizerDiscardDoesNotInterfere) { +#if !BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) + GTEST_SKIP() << "Test only reproduces with validating decoder"; +#else + if (gl_.gpu_preferences().use_passthrough_cmd_decoder) { + GTEST_SKIP() << "Skipping test because it's run with the passthrough " + "command decoder"; + } + + if (!gl_.IsInitialized()) { + GTEST_SKIP() << "ES3 context unavailable"; + } + + constexpr GLsizei kW = 64, kH = 64; + + // --- Step 1: prime VRAM with a recognisable pattern, then free it. --- + { + GLuint prime; + glGenTextures(1, &prime); + glBindTexture(GL_TEXTURE_2D, prime); + std::vector<uint8_t> pat(kW * kH * 4, 0xCA); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kW, kH, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pat.data()); + glFinish(); + glDeleteTextures(1, &prime); + } + + // --- Step 2: dest texture, allocated WITHOUT data → uninitialized VRAM. --- + GLuint dest; + glGenTextures(1, &dest); + glBindTexture(GL_TEXTURE_2D, dest); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kW, kH, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + + // --- Step 3: trivial source texture (contents irrelevant). --- + GLuint src; + glGenTextures(1, &src); + glBindTexture(GL_TEXTURE_2D, src); + std::vector<uint8_t> green(kW * kH * 4, 0); + for (size_t i = 0; i < green.size(); i += 4) { + green[i + 1] = 0xFF; // G + green[i + 3] = 0xFF; // A + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kW, kH, 0, GL_RGBA, GL_UNSIGNED_BYTE, + green.data()); + + // --- Step 4: enable RASTERIZER_DISCARD on the command-buffer context. --- + glEnable(GL_RASTERIZER_DISCARD); + ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); + + // --- Step 5: issue the copy. flip_y=GL_TRUE forces a draw-based path --- + CopyType copy_type = GetParam(); + if (copy_type == TexImage) { + glCopyTextureCHROMIUM(src, 0, GL_TEXTURE_2D, dest, 0, GL_RGBA8, + GL_UNSIGNED_BYTE, GL_TRUE, GL_FALSE, GL_FALSE); + } else { + glCopySubTextureCHROMIUM(src, 0, GL_TEXTURE_2D, dest, 0, 0, 0, 0, 0, kW, kH, + GL_TRUE, GL_FALSE, GL_FALSE); + } + ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); + + glDisable(GL_RASTERIZER_DISCARD); + + // --- Step 6: read back. --- + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + dest, 0); + ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + std::vector<uint8_t> pixels(kW * kH * 4, 0); + glReadPixels(0, 0, kW, kH, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); + + // --- Step 7: prove the leak is gone (copy succeeded). --- + size_t nonzero = 0, green_px = 0; + for (size_t i = 0; i < pixels.size(); i += 4) { + if (pixels[i] || pixels[i + 1] || pixels[i + 2] || pixels[i + 3]) { + ++nonzero; + } + if (pixels[i] == 0x00 && pixels[i + 1] == 0xFF && pixels[i + 2] == 0x00 && + pixels[i + 3] == 0xFF) { + ++green_px; + } + } + + // Expect all pixels to be green. + EXPECT_EQ(green_px, static_cast<size_t>(kW * kH)); + EXPECT_GT(nonzero, 0u); + + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &src); + glDeleteTextures(1, &dest); +#endif // !BUILDFLAG(ENABLE_VALIDATING_COMMAND_DECODER) +} + } // namespace gpu