Destroy frontend and backend for Textures

Same idea as for buffers, Destroy can be used to free GPU memory
associated with resources without waiting for javascript garbage
collection to occur.

Bug: dawn:46
Change-Id: Ia796b06b5228cbec4cfe8d78a500f967181d8c1f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/5540
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Natasha Lee <natlee@microsoft.com>
This commit is contained in:
Natasha Lee 2019-03-27 22:04:10 +00:00 committed by Commit Bot service account
parent 889d743baa
commit cae68ff846
17 changed files with 235 additions and 52 deletions

View File

@ -636,7 +636,7 @@ test("dawn_end2end_tests") {
"src/tests/end2end/CopyTests.cpp", "src/tests/end2end/CopyTests.cpp",
"src/tests/end2end/DebugMarkerTests.cpp", "src/tests/end2end/DebugMarkerTests.cpp",
"src/tests/end2end/DepthStencilStateTests.cpp", "src/tests/end2end/DepthStencilStateTests.cpp",
"src/tests/end2end/DestroyBufferTests.cpp", "src/tests/end2end/DestroyTests.cpp",
"src/tests/end2end/DrawIndexedTests.cpp", "src/tests/end2end/DrawIndexedTests.cpp",
"src/tests/end2end/DrawTests.cpp", "src/tests/end2end/DrawTests.cpp",
"src/tests/end2end/FenceTests.cpp", "src/tests/end2end/FenceTests.cpp",

View File

@ -986,6 +986,9 @@
"args": [ "args": [
{"name": "descriptor", "type": "texture view descriptor", "annotation": "const*"} {"name": "descriptor", "type": "texture view descriptor", "annotation": "const*"}
] ]
},
{
"name": "destroy"
} }
] ]
}, },

View File

@ -197,6 +197,9 @@ namespace dawn_native {
if (descriptor->nextInChain != nullptr) { if (descriptor->nextInChain != nullptr) {
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
} }
if (texture->GetTextureState() == TextureBase::TextureState::Destroyed) {
return DAWN_VALIDATION_ERROR("Destroyed texture used to create texture view");
}
DAWN_TRY(device->ValidateObject(texture)); DAWN_TRY(device->ValidateObject(texture));
DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension)); DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension));
@ -322,7 +325,9 @@ namespace dawn_native {
// TextureBase // TextureBase
TextureBase::TextureBase(DeviceBase* device, const TextureDescriptor* descriptor) TextureBase::TextureBase(DeviceBase* device,
const TextureDescriptor* descriptor,
TextureState state)
: ObjectBase(device), : ObjectBase(device),
mDimension(descriptor->dimension), mDimension(descriptor->dimension),
mFormat(descriptor->format), mFormat(descriptor->format),
@ -330,7 +335,8 @@ namespace dawn_native {
mArrayLayerCount(descriptor->arrayLayerCount), mArrayLayerCount(descriptor->arrayLayerCount),
mMipLevelCount(descriptor->mipLevelCount), mMipLevelCount(descriptor->mipLevelCount),
mSampleCount(descriptor->sampleCount), mSampleCount(descriptor->sampleCount),
mUsage(descriptor->usage) { mUsage(descriptor->usage),
mState(state) {
} }
TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag) TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag)
@ -371,8 +377,24 @@ namespace dawn_native {
return mUsage; return mUsage;
} }
TextureBase::TextureState TextureBase::GetTextureState() const {
ASSERT(!IsError());
return mState;
}
MaybeError TextureBase::ValidateCanUseInSubmitNow() const { MaybeError TextureBase::ValidateCanUseInSubmitNow() const {
ASSERT(!IsError()); ASSERT(!IsError());
if (mState == TextureState::Destroyed) {
return DAWN_VALIDATION_ERROR("Destroyed texture used in a submit");
}
return {};
}
MaybeError TextureBase::ValidateCanCreateTextureViewNow() const {
ASSERT(!IsError());
if (mState == TextureState::Destroyed) {
return DAWN_VALIDATION_ERROR("Destroyed texture used to create texture view");
}
return {}; return {};
} }
@ -395,6 +417,16 @@ namespace dawn_native {
return GetDevice()->CreateTextureView(this, descriptor); return GetDevice()->CreateTextureView(this, descriptor);
} }
void TextureBase::Destroy() {
if (mState == TextureState::OwnedInternal) {
DestroyImpl();
}
mState = TextureState::Destroyed;
}
void TextureBase::DestroyImpl() {
}
// TextureViewBase // TextureViewBase
TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor) TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor)

View File

@ -46,7 +46,9 @@ namespace dawn_native {
class TextureBase : public ObjectBase { class TextureBase : public ObjectBase {
public: public:
TextureBase(DeviceBase* device, const TextureDescriptor* descriptor); enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
TextureBase(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state);
static TextureBase* MakeError(DeviceBase* device); static TextureBase* MakeError(DeviceBase* device);
@ -57,17 +59,21 @@ namespace dawn_native {
uint32_t GetNumMipLevels() const; uint32_t GetNumMipLevels() const;
uint32_t GetSampleCount() const; uint32_t GetSampleCount() const;
dawn::TextureUsageBit GetUsage() const; dawn::TextureUsageBit GetUsage() const;
TextureState GetTextureState() const;
MaybeError ValidateCanUseInSubmitNow() const; MaybeError ValidateCanUseInSubmitNow() const;
MaybeError ValidateCanCreateTextureViewNow() const;
bool IsMultisampledTexture() const; bool IsMultisampledTexture() const;
// Dawn API // Dawn API
TextureViewBase* CreateDefaultTextureView(); TextureViewBase* CreateDefaultTextureView();
TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor); TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor);
void Destroy();
private: private:
TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag); TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag);
virtual void DestroyImpl();
dawn::TextureDimension mDimension; dawn::TextureDimension mDimension;
dawn::TextureFormat mFormat; dawn::TextureFormat mFormat;
@ -76,6 +82,7 @@ namespace dawn_native {
uint32_t mMipLevelCount; uint32_t mMipLevelCount;
uint32_t mSampleCount; uint32_t mSampleCount;
dawn::TextureUsageBit mUsage = dawn::TextureUsageBit::None; dawn::TextureUsageBit mUsage = dawn::TextureUsageBit::None;
TextureState mState;
}; };
class TextureViewBase : public ObjectBase { class TextureViewBase : public ObjectBase {

View File

@ -108,7 +108,7 @@ namespace dawn_native { namespace d3d12 {
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor) Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor) { : TextureBase(device, descriptor, TextureState::OwnedInternal) {
D3D12_RESOURCE_DESC resourceDescriptor; D3D12_RESOURCE_DESC resourceDescriptor;
resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension()); resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension());
resourceDescriptor.Alignment = 0; resourceDescriptor.Alignment = 0;
@ -136,14 +136,18 @@ namespace dawn_native { namespace d3d12 {
Texture::Texture(Device* device, Texture::Texture(Device* device,
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
ID3D12Resource* nativeTexture) ID3D12Resource* nativeTexture)
: TextureBase(device, descriptor), mResourcePtr(nativeTexture) { : TextureBase(device, descriptor, TextureState::OwnedExternal),
mResourcePtr(nativeTexture) {
} }
Texture::~Texture() { Texture::~Texture() {
if (mResource) { Destroy();
// If we own the resource, release it. }
ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
} void Texture::DestroyImpl() {
// If we own the resource, release it.
ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource);
mResource = nullptr;
} }
DXGI_FORMAT Texture::GetD3D12Format() const { DXGI_FORMAT Texture::GetD3D12Format() const {

View File

@ -38,6 +38,9 @@ namespace dawn_native { namespace d3d12 {
dawn::TextureUsageBit usage); dawn::TextureUsageBit usage);
private: private:
// Dawn API
void DestroyImpl() override;
UINT16 GetDepthOrArraySize(); UINT16 GetDepthOrArraySize();
ComPtr<ID3D12Resource> mResource = {}; ComPtr<ID3D12Resource> mResource = {};

View File

@ -42,6 +42,8 @@ namespace dawn_native { namespace metal {
id<MTLTexture> GetMTLTexture(); id<MTLTexture> GetMTLTexture();
private: private:
void DestroyImpl() override;
id<MTLTexture> mMtlTexture = nil; id<MTLTexture> mMtlTexture = nil;
}; };

View File

@ -194,14 +194,14 @@ namespace dawn_native { namespace metal {
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor) Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor) { : TextureBase(device, descriptor, TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor); MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc]; mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
[mtlDesc release]; [mtlDesc release];
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture) Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture)
: TextureBase(device, descriptor), mMtlTexture(mtlTexture) { : TextureBase(device, descriptor, TextureState::OwnedInternal), mMtlTexture(mtlTexture) {
[mMtlTexture retain]; [mMtlTexture retain];
} }
@ -209,7 +209,7 @@ namespace dawn_native { namespace metal {
const TextureDescriptor* descriptor, const TextureDescriptor* descriptor,
IOSurfaceRef ioSurface, IOSurfaceRef ioSurface,
uint32_t plane) uint32_t plane)
: TextureBase(device, descriptor) { : TextureBase(device, descriptor, TextureState::OwnedInternal) {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor); MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
mtlDesc.storageMode = MTLStorageModeManaged; mtlDesc.storageMode = MTLStorageModeManaged;
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc
@ -219,7 +219,12 @@ namespace dawn_native { namespace metal {
} }
Texture::~Texture() { Texture::~Texture() {
Destroy();
}
void Texture::DestroyImpl() {
[mMtlTexture release]; [mMtlTexture release];
mMtlTexture = nil;
} }
id<MTLTexture> Texture::GetMTLTexture() { id<MTLTexture> Texture::GetMTLTexture() {

View File

@ -110,7 +110,7 @@ namespace dawn_native { namespace null {
return new SwapChain(this, descriptor); return new SwapChain(this, descriptor);
} }
ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { ResultOrError<TextureBase*> Device::CreateTextureImpl(const TextureDescriptor* descriptor) {
return new Texture(this, descriptor); return new Texture(this, descriptor, TextureBase::TextureState::OwnedInternal);
} }
ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl( ResultOrError<TextureViewBase*> Device::CreateTextureViewImpl(
TextureBase* texture, TextureBase* texture,

View File

@ -40,7 +40,8 @@ namespace dawn_native { namespace opengl {
return nullptr; return nullptr;
} }
GLuint nativeTexture = next.texture.u32; GLuint nativeTexture = next.texture.u32;
return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture); return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture,
TextureBase::TextureState::OwnedExternal);
} }
void SwapChain::OnBeforePresent(TextureBase*) { void SwapChain::OnBeforePresent(TextureBase*) {

View File

@ -119,7 +119,7 @@ namespace dawn_native { namespace opengl {
// Texture // Texture
Texture::Texture(Device* device, const TextureDescriptor* descriptor) Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: Texture(device, descriptor, GenTexture()) { : Texture(device, descriptor, GenTexture(), TextureState::OwnedInternal) {
uint32_t width = GetSize().width; uint32_t width = GetSize().width;
uint32_t height = GetSize().height; uint32_t height = GetSize().height;
uint32_t levels = GetNumMipLevels(); uint32_t levels = GetNumMipLevels();
@ -150,14 +150,21 @@ namespace dawn_native { namespace opengl {
glTexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1); glTexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle) Texture::Texture(Device* device,
: TextureBase(device, descriptor), mHandle(handle) { const TextureDescriptor* descriptor,
GLuint handle,
TextureState state)
: TextureBase(device, descriptor, state), mHandle(handle) {
mTarget = TargetForDimensionAndArrayLayers(GetDimension(), GetArrayLayers()); mTarget = TargetForDimensionAndArrayLayers(GetDimension(), GetArrayLayers());
} }
Texture::~Texture() { Texture::~Texture() {
// TODO(kainino@chromium.org): delete texture (but only when not using the native texture Destroy();
// constructor?) }
void Texture::DestroyImpl() {
glDeleteTextures(1, &mHandle);
mHandle = 0;
} }
GLuint Texture::GetHandle() const { GLuint Texture::GetHandle() const {

View File

@ -32,7 +32,10 @@ namespace dawn_native { namespace opengl {
class Texture : public TextureBase { class Texture : public TextureBase {
public: public:
Texture(Device* device, const TextureDescriptor* descriptor); Texture(Device* device, const TextureDescriptor* descriptor);
Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle); Texture(Device* device,
const TextureDescriptor* descriptor,
GLuint handle,
TextureState state);
~Texture(); ~Texture();
GLuint GetHandle() const; GLuint GetHandle() const;
@ -40,6 +43,8 @@ namespace dawn_native { namespace opengl {
TextureFormatInfo GetGLFormat() const; TextureFormatInfo GetGLFormat() const;
private: private:
void DestroyImpl() override;
GLuint mHandle; GLuint mHandle;
GLenum mTarget; GLenum mTarget;
}; };

View File

@ -246,7 +246,7 @@ namespace dawn_native { namespace vulkan {
} }
Texture::Texture(Device* device, const TextureDescriptor* descriptor) Texture::Texture(Device* device, const TextureDescriptor* descriptor)
: TextureBase(device, descriptor) { : TextureBase(device, descriptor, TextureState::OwnedInternal) {
// Create the Vulkan image "container". We don't need to check that the format supports the // Create the Vulkan image "container". We don't need to check that the format supports the
// combination of sample, usage etc. because validation should have been done in the Dawn // combination of sample, usage etc. because validation should have been done in the Dawn
// frontend already based on the minimum supported formats in the Vulkan spec // frontend already based on the minimum supported formats in the Vulkan spec
@ -291,17 +291,23 @@ namespace dawn_native { namespace vulkan {
} }
} }
// With this constructor, the lifetime of the resource is externally managed.
Texture::Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage) Texture::Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage)
: TextureBase(device, descriptor), mHandle(nativeImage) { : TextureBase(device, descriptor, TextureState::OwnedExternal), mHandle(nativeImage) {
} }
Texture::~Texture() { Texture::~Texture() {
Destroy();
}
void Texture::DestroyImpl() {
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
// If we own the resource, release it. // If we own the resource, release it.
if (mMemoryAllocation.GetMemory() != VK_NULL_HANDLE) { if (mMemoryAllocation.GetMemory() != VK_NULL_HANDLE) {
// We need to free both the memory allocation and the container. Memory should be freed // We need to free both the memory allocation and the container. Memory should be
// after the VkImage is destroyed and this is taken care of by the FencedDeleter. // freed after the VkImage is destroyed and this is taken care of by the
// FencedDeleter.
device->GetMemoryAllocator()->Free(&mMemoryAllocation); device->GetMemoryAllocator()->Free(&mMemoryAllocation);
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {

View File

@ -40,6 +40,8 @@ namespace dawn_native { namespace vulkan {
void TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage); void TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage);
private: private:
void DestroyImpl() override;
VkImage mHandle = VK_NULL_HANDLE; VkImage mHandle = VK_NULL_HANDLE;
DeviceMemoryAllocation mMemoryAllocation; DeviceMemoryAllocation mMemoryAllocation;

View File

@ -19,7 +19,7 @@
constexpr uint32_t kRTSize = 4; constexpr uint32_t kRTSize = 4;
class DestroyBufferTest : public DawnTest { class DestroyTest : public DawnTest {
protected: protected:
void SetUp() override { void SetUp() override {
DawnTest::SetUp(); DawnTest::SetUp();
@ -96,7 +96,7 @@ class DestroyBufferTest : public DawnTest {
}; };
// Destroy before submit will result in error, and nothing drawn // Destroy before submit will result in error, and nothing drawn
TEST_P(DestroyBufferTest, DestroyBeforeSubmit) { TEST_P(DestroyTest, BufferDestroyBeforeSubmit) {
RGBA8 notFilled(0, 0, 0, 0); RGBA8 notFilled(0, 0, 0, 0);
dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
@ -107,7 +107,7 @@ TEST_P(DestroyBufferTest, DestroyBeforeSubmit) {
} }
// Destroy after submit will draw successfully // Destroy after submit will draw successfully
TEST_P(DestroyBufferTest, DestroyAfterSubmit) { TEST_P(DestroyTest, BufferDestroyAfterSubmit) {
RGBA8 filled(0, 255, 0, 255); RGBA8 filled(0, 255, 0, 255);
dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
@ -119,7 +119,7 @@ TEST_P(DestroyBufferTest, DestroyAfterSubmit) {
// First submit succeeds, draws triangle, second submit fails // First submit succeeds, draws triangle, second submit fails
// after destroy is called on the buffer, pixel does not change // after destroy is called on the buffer, pixel does not change
TEST_P(DestroyBufferTest, SubmitDestroySubmit) { TEST_P(DestroyTest, BufferSubmitDestroySubmit) {
RGBA8 filled(0, 255, 0, 255); RGBA8 filled(0, 255, 0, 255);
dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
@ -135,4 +135,37 @@ TEST_P(DestroyBufferTest, SubmitDestroySubmit) {
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
} }
DAWN_INSTANTIATE_TEST(DestroyBufferTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); // Destroy texture before submit should fail submit
TEST_P(DestroyTest, TextureDestroyBeforeSubmit) {
dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
renderPass.color.Destroy();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
// Destroy after submit will draw successfully
TEST_P(DestroyTest, TextureDestroyAfterSubmit) {
RGBA8 filled(0, 255, 0, 255);
dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
renderPass.color.Destroy();
}
// First submit succeeds, draws triangle, second submit fails
// after destroy is called on the texture
TEST_P(DestroyTest, TextureSubmitDestroySubmit) {
RGBA8 filled(0, 255, 0, 255);
dawn::CommandBuffer commands = CreateTriangleCommandBuffer();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
renderPass.color.Destroy();
// Submit fails because texture was destroyed
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
DAWN_INSTANTIATE_TEST(DestroyTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);

View File

@ -15,35 +15,39 @@
#include "tests/unittests/validation/ValidationTest.h" #include "tests/unittests/validation/ValidationTest.h"
#include "common/Constants.h" #include "common/Constants.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h" #include "utils/DawnHelpers.h"
namespace { namespace {
class TextureValidationTest : public ValidationTest { class TextureValidationTest : public ValidationTest {
protected:
dawn::TextureDescriptor CreateDefaultTextureDescriptor() {
dawn::TextureDescriptor descriptor;
descriptor.size.width = kWidth;
descriptor.size.height = kHeight;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = kDefaultArraySize;
descriptor.mipLevelCount = kDefaultMipLevels;
descriptor.sampleCount = kDefaultSampleCount;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.format = kDefaultTextureFormat;
descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::Sampled;
return descriptor;
}
dawn::Queue queue = device.CreateQueue();
private:
static constexpr uint32_t kWidth = 32;
static constexpr uint32_t kHeight = 32;
static constexpr uint32_t kDefaultArraySize = 1;
static constexpr uint32_t kDefaultMipLevels = 1;
static constexpr uint32_t kDefaultSampleCount = 1;
static constexpr dawn::TextureFormat kDefaultTextureFormat = dawn::TextureFormat::R8G8B8A8Unorm;
}; };
constexpr uint32_t kWidth = 32;
constexpr uint32_t kHeight = 32;
constexpr uint32_t kDefaultArraySize = 1;
constexpr uint32_t kDefaultMipLevels = 1;
constexpr uint32_t kDefaultSampleCount = 1;
constexpr dawn::TextureFormat kDefaultTextureFormat = dawn::TextureFormat::R8G8B8A8Unorm;
dawn::TextureDescriptor CreateDefaultTextureDescriptor() {
dawn::TextureDescriptor descriptor;
descriptor.size.width = kWidth;
descriptor.size.height = kHeight;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = kDefaultArraySize;
descriptor.mipLevelCount = kDefaultMipLevels;
descriptor.sampleCount = kDefaultSampleCount;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.format = kDefaultTextureFormat;
descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::Sampled;
return descriptor;
}
// Test the validation of sample count // Test the validation of sample count
TEST_F(TextureValidationTest, SampleCount) { TEST_F(TextureValidationTest, SampleCount) {
dawn::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); dawn::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor();
@ -81,4 +85,65 @@ TEST_F(TextureValidationTest, SampleCount) {
ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
} }
} }
// Test that it is valid to destroy a texture
TEST_F(TextureValidationTest, DestroyTexture) {
dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
dawn::Texture texture = device.CreateTexture(&descriptor);
texture.Destroy();
}
// Test that it's valid to destroy a destroyed texture
TEST_F(TextureValidationTest, DestroyDestroyedTexture) {
dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
dawn::Texture texture = device.CreateTexture(&descriptor);
texture.Destroy();
texture.Destroy();
}
// Test that it's invalid to submit a destroyed texture in a queue
// in the case of destroy, encode, submit
TEST_F(TextureValidationTest, DestroyEncodeSubmit) {
dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
dawn::Texture texture = device.CreateTexture(&descriptor);
dawn::TextureView textureView = texture.CreateDefaultTextureView();
utils::ComboRenderPassDescriptor renderPass({textureView});
// Destroy the texture
texture.Destroy();
dawn::CommandEncoder encoder_post_destroy = device.CreateCommandEncoder();
{
dawn::RenderPassEncoder pass = encoder_post_destroy.BeginRenderPass(&renderPass);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder_post_destroy.Finish();
// Submit should fail due to destroyed texture
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
// Test that it's invalid to submit a destroyed texture in a queue
// in the case of encode, destroy, submit
TEST_F(TextureValidationTest, EncodeDestroySubmit) {
dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();
dawn::Texture texture = device.CreateTexture(&descriptor);
dawn::TextureView textureView = texture.CreateDefaultTextureView();
utils::ComboRenderPassDescriptor renderPass({textureView});
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
{
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder.Finish();
// Destroy the texture
texture.Destroy();
// Submit should fail due to destroyed texture
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
} // namespace } // namespace

View File

@ -242,4 +242,12 @@ TEST_F(TextureViewValidationTest, TextureViewFormatCompatibility) {
} }
} }
// Test that it's invalid to create a texture view from a destroyed texture
TEST_F(TextureViewValidationTest, DestroyCreateTextureView) {
dawn::Texture texture = Create2DArrayTexture(device, 1);
dawn::TextureViewDescriptor descriptor =
CreateDefaultTextureViewDescriptor(dawn::TextureViewDimension::e2D);
texture.Destroy();
ASSERT_DEVICE_ERROR(texture.CreateTextureView(&descriptor));
}
} }