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>
This commit is contained in:
Shrek Shao 2022-05-13 21:05:57 +00:00 committed by Dawn LUCI CQ
parent 83fc247d4b
commit 8faaad9b5e
3 changed files with 63 additions and 28 deletions

View File

@ -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();

View File

@ -79,10 +79,13 @@ class EncodingContext {
}
inline bool CheckCurrentEncoder(const ApiObjectBase* encoder) {
if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
if (mDestroyed) {
HandleError(DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", encoder));
} else if (mCurrentEncoder != mTopLevelEncoder) {
HandleError(
DAWN_FORMAT_VALIDATION_ERROR("Recording in a destroyed %s.", mCurrentEncoder));
return false;
}
if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) {
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));

View File

@ -31,12 +31,12 @@ TEST_F(CommandBufferValidationTest, Empty) {
// 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 @@ TEST_F(CommandBufferValidationTest, EndedMidRenderPass) {
// 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 @@ TEST_F(CommandBufferValidationTest, EndedMidRenderPass) {
// 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_F(CommandBufferValidationTest, EndedMidComputePass) {
// 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 @@ TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) {
// 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_F(CommandBufferValidationTest, ComputePassEndedTwice) {
// 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_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) {
// 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 @@ TEST_F(CommandBufferValidationTest, BeginRenderPassBeforeEndPreviousPass) {
{
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_F(CommandBufferValidationTest, CallsAfterAFailedFinish) {
// 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 @@ TEST_F(CommandBufferValidationTest, PassDereferenced) {
// 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 @@ TEST_F(CommandBufferValidationTest, PassDereferenced) {
// 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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
// 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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
// 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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
// 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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
{
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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
{
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 @@ TEST_F(CommandBufferValidationTest, DestroyEncoder) {
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."));
}
}