Validate if device is alive in EncodeFunction of EncodingContext::TryEncode
GetOrCreateAttachmentState access mCaches in Device even if device is
already destroyed. Gate the function by checking if encoder is
destroyed.
Bug: chromium:1323310
Change-Id: I8151c5ec696e4da28b8296d9142f3120379782ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89860
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/EncodingContext.cpp b/src/dawn/native/EncodingContext.cpp
index 1a91a8e..d222927 100644
--- a/src/dawn/native/EncodingContext.cpp
+++ b/src/dawn/native/EncodingContext.cpp
@@ -25,7 +25,10 @@
namespace dawn::native {
EncodingContext::EncodingContext(DeviceBase* device, const ApiObjectBase* initialEncoder)
- : mDevice(device), mTopLevelEncoder(initialEncoder), mCurrentEncoder(initialEncoder) {}
+ : mDevice(device),
+ mTopLevelEncoder(initialEncoder),
+ mCurrentEncoder(initialEncoder),
+ mDestroyed(device->IsLost()) {}
EncodingContext::~EncodingContext() {
Destroy();
diff --git a/src/dawn/native/EncodingContext.h b/src/dawn/native/EncodingContext.h
index 360790a..020132e 100644
--- a/src/dawn/native/EncodingContext.h
+++ b/src/dawn/native/EncodingContext.h
@@ -79,10 +79,13 @@
}
inline bool CheckCurrentEncoder(const ApiObjectBase* encoder) {
+ if (mDestroyed) {
+ HandleError(
+ DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", mCurrentEncoder));
+ return false;
+ }
if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
- if (mDestroyed) {
- HandleError(DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", encoder));
- } else if (mCurrentEncoder != mTopLevelEncoder) {
+ if (mCurrentEncoder != mTopLevelEncoder) {
// The top level encoder was used when a pass encoder was current.
HandleError(DAWN_FORMAT_VALIDATION_ERROR(
"Command cannot be recorded while %s is active.", mCurrentEncoder));
diff --git a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
index 0b74c72..dc45bc0 100644
--- a/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/CommandBufferValidationTests.cpp
@@ -31,12 +31,12 @@
// Test that a command buffer cannot be ended mid render pass
TEST_F(CommandBufferValidationTest, EndedMidRenderPass) {
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Control case, command buffer ended after the pass is ended.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
encoder.Finish();
}
@@ -44,7 +44,7 @@
// Error case, command buffer ended mid-pass.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
ASSERT_DEVICE_ERROR(
encoder.Finish(),
HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -54,7 +54,7 @@
// should fail too.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
ASSERT_DEVICE_ERROR(
encoder.Finish(),
HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -97,12 +97,12 @@
// Test that a render pass cannot be ended twice
TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) {
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Control case, pass is ended once
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
encoder.Finish();
}
@@ -110,7 +110,7 @@
// Error case, pass ended twice
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
pass.End();
ASSERT_DEVICE_ERROR(
@@ -143,12 +143,12 @@
// Test that beginning a compute pass before ending the previous pass causes an error.
TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) {
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Beginning a compute pass before ending a render pass causes an error.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&placeholderRenderPass);
wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
computePass.End();
renderPass.End();
@@ -168,13 +168,13 @@
// Test that beginning a render pass before ending the previous pass causes an error.
TEST_F(CommandBufferValidationTest, BeginRenderPassBeforeEndPreviousPass) {
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Beginning a render pass before ending the render pass causes an error.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder renderPass1 = encoder.BeginRenderPass(&PlaceholderRenderPass);
- wgpu::RenderPassEncoder renderPass2 = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder renderPass1 = encoder.BeginRenderPass(&placeholderRenderPass);
+ wgpu::RenderPassEncoder renderPass2 = encoder.BeginRenderPass(&placeholderRenderPass);
renderPass2.End();
renderPass1.End();
ASSERT_DEVICE_ERROR(encoder.Finish());
@@ -184,7 +184,7 @@
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder computePass = encoder.BeginComputePass();
- wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&placeholderRenderPass);
renderPass.End();
computePass.End();
ASSERT_DEVICE_ERROR(encoder.Finish());
@@ -229,12 +229,12 @@
// Test that passes which are de-referenced prior to ending still allow the correct errors to be
// produced.
TEST_F(CommandBufferValidationTest, PassDereferenced) {
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Control case, command buffer ended after the pass is ended.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
encoder.Finish();
}
@@ -242,7 +242,7 @@
// Error case, no reference is kept to a render pass.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- encoder.BeginRenderPass(&PlaceholderRenderPass);
+ encoder.BeginRenderPass(&placeholderRenderPass);
ASSERT_DEVICE_ERROR(
encoder.Finish(),
HasSubstr("Command buffer recording ended before [RenderPassEncoder] was ended."));
@@ -260,7 +260,7 @@
// Error case, beginning a new pass after failing to end a de-referenced pass.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- encoder.BeginRenderPass(&PlaceholderRenderPass);
+ encoder.BeginRenderPass(&placeholderRenderPass);
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
pass.End();
ASSERT_DEVICE_ERROR(
@@ -301,12 +301,12 @@
// only way to trigger the destroy call is by losing all references which means we cannot
// call finish.
DAWN_SKIP_TEST_IF(UsesWire());
- PlaceholderRenderPass PlaceholderRenderPass(device);
+ PlaceholderRenderPass placeholderRenderPass(device);
// Control case, command buffer ended after the pass is ended.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
encoder.Finish();
}
@@ -314,7 +314,7 @@
// Destroyed encoder with encoded commands should emit error on finish.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
dawn::native::FromAPI(encoder.Get())->Destroy();
ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
@@ -323,7 +323,7 @@
// Destroyed encoder with encoded commands shouldn't emit an error if never finished.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
dawn::native::FromAPI(encoder.Get())->Destroy();
}
@@ -332,7 +332,7 @@
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::native::FromAPI(encoder.Get())->Destroy();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
}
@@ -341,14 +341,14 @@
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::native::FromAPI(encoder.Get())->Destroy();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
}
// Destroying a finished encoder should not emit any errors.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
- wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&PlaceholderRenderPass);
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
pass.End();
encoder.Finish();
dawn::native::FromAPI(encoder.Get())->Destroy();
@@ -369,3 +369,32 @@
ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
}
}
+
+TEST_F(CommandBufferValidationTest, EncodeAfterDeviceDestroyed) {
+ PlaceholderRenderPass placeholderRenderPass(device);
+
+ // Device destroyed before encoding.
+ {
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ ExpectDeviceDestruction();
+ device.Destroy();
+ // The encoder should not accessing any device info if device is destroyed when try
+ // encoding.
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
+ pass.End();
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("Destroyed encoder cannot be finished."));
+ }
+
+ // Device destroyed after encoding.
+ {
+ ExpectDeviceDestruction();
+ device.Destroy();
+ ASSERT_DEVICE_ERROR(wgpu::CommandEncoder encoder = device.CreateCommandEncoder(),
+ HasSubstr("[Device] is lost"));
+ // The encoder should not accessing any device info if device is destroyed when try
+ // encoding.
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&placeholderRenderPass);
+ pass.End();
+ ASSERT_DEVICE_ERROR(encoder.Finish(), HasSubstr("[Invalid CommandEncoder] is invalid."));
+ }
+}