Refactors Destroy to be upward calls instead of overriding.

- Fixes all class and implementations to call upwards when necessary.
- Makes DestroyImpl full virtual at ApiObjectBase.
- Fixes mock classes to call default DestroyImpl upwards on mock calls.
- Adds back D3D12 optimization for buffer destruction.

Bug: dawn:628, dawn:1189
Change-Id: Id2c2c6483dc7ed93daf8e4648af26ac1307e3d90
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/71243
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Loko Kung 2021-12-01 18:54:40 +00:00 committed by Dawn LUCI CQ
parent 2494c9be8f
commit 6331f959c1
78 changed files with 148 additions and 107 deletions

View File

@ -408,6 +408,9 @@ namespace dawn_native {
} }
} }
void BindGroupBase::DestroyImpl() {
}
void BindGroupBase::DeleteThis() { void BindGroupBase::DeleteThis() {
// Add another ref to the layout so that if this is the last ref, the layout // Add another ref to the layout so that if this is the last ref, the layout
// is destroyed after the bind group. The bind group is slab-allocated inside // is destroyed after the bind group. The bind group is slab-allocated inside

View File

@ -75,6 +75,7 @@ namespace dawn_native {
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
BindGroupBase(DeviceBase* device); BindGroupBase(DeviceBase* device);
void DestroyImpl() override;
~BindGroupBase() override; ~BindGroupBase() override;

View File

@ -418,13 +418,11 @@ namespace dawn_native {
BindGroupLayoutBase::~BindGroupLayoutBase() = default; BindGroupLayoutBase::~BindGroupLayoutBase() = default;
bool BindGroupLayoutBase::Destroy() { void BindGroupLayoutBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint or already destroyed.
GetDevice()->UncacheBindGroupLayout(this); GetDevice()->UncacheBindGroupLayout(this);
} }
return wasDestroyed;
} }
// static // static

View File

@ -53,7 +53,6 @@ namespace dawn_native {
static BindGroupLayoutBase* MakeError(DeviceBase* device); static BindGroupLayoutBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
// A map from the BindingNumber to its packed BindingIndex. // A map from the BindingNumber to its packed BindingIndex.
@ -118,6 +117,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
BindGroupLayoutBase(DeviceBase* device); BindGroupLayoutBase(DeviceBase* device);
void DestroyImpl() override;
template <typename BindGroup> template <typename BindGroup>
SlabAllocator<BindGroup> MakeFrontendBindGroupAllocator(size_t size) { SlabAllocator<BindGroup> MakeFrontendBindGroupAllocator(size_t size) {

View File

@ -91,10 +91,6 @@ namespace dawn_native {
mFakeMappedData.reset(); mFakeMappedData.reset();
} }
void DestroyImpl() override {
mFakeMappedData.reset();
}
std::unique_ptr<uint8_t[]> mFakeMappedData; std::unique_ptr<uint8_t[]> mFakeMappedData;
}; };
@ -183,12 +179,7 @@ namespace dawn_native {
ASSERT(mState == BufferState::Unmapped || mState == BufferState::Destroyed); ASSERT(mState == BufferState::Unmapped || mState == BufferState::Destroyed);
} }
bool BufferBase::Destroy() { void BufferBase::DestroyImpl() {
bool marked = MarkDestroyed();
if (!marked) {
return false;
}
if (mState == BufferState::Mapped) { if (mState == BufferState::Mapped) {
UnmapInternal(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback); UnmapInternal(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
} else if (mState == BufferState::MappedAtCreation) { } else if (mState == BufferState::MappedAtCreation) {
@ -198,10 +189,7 @@ namespace dawn_native {
UnmapInternal(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback); UnmapInternal(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
} }
} }
DestroyImpl();
mState = BufferState::Destroyed; mState = BufferState::Destroyed;
return true;
} }
// static // static

View File

@ -52,7 +52,6 @@ namespace dawn_native {
static BufferBase* MakeError(DeviceBase* device, const BufferDescriptor* descriptor); static BufferBase* MakeError(DeviceBase* device, const BufferDescriptor* descriptor);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
uint64_t GetSize() const; uint64_t GetSize() const;
@ -89,6 +88,7 @@ namespace dawn_native {
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
BufferBase(DeviceBase* device, BufferState state); BufferBase(DeviceBase* device, BufferState state);
void DestroyImpl() override;
~BufferBase() override; ~BufferBase() override;

View File

@ -47,14 +47,13 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
CommandBufferBase(DeviceBase* device); CommandBufferBase(DeviceBase* device);
void DestroyImpl() override;
CommandIterator mCommands; CommandIterator mCommands;
private: private:
CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag); CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag);
void DestroyImpl() override;
CommandBufferResourceUsage mResourceUsages; CommandBufferResourceUsage mResourceUsages;
}; };

View File

@ -133,7 +133,6 @@ namespace dawn_native {
} }
void ComputePassEncoder::DestroyImpl() { void ComputePassEncoder::DestroyImpl() {
ApiObjectBase::DestroyImpl();
// Ensure that the pass has exited. This is done for passes only since validation requires // Ensure that the pass has exited. This is done for passes only since validation requires
// they exit before destruction while bundles do not. // they exit before destruction while bundles do not.
mEncodingContext->EnsurePassExited(this); mEncodingContext->EnsurePassExited(this);

View File

@ -60,13 +60,11 @@ namespace dawn_native {
ComputePipelineBase::~ComputePipelineBase() = default; ComputePipelineBase::~ComputePipelineBase() = default;
bool ComputePipelineBase::Destroy() { void ComputePipelineBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint or already destroyed.
GetDevice()->UncacheComputePipeline(this); GetDevice()->UncacheComputePipeline(this);
} }
return wasDestroyed;
} }
// static // static

View File

@ -34,7 +34,6 @@ namespace dawn_native {
static ComputePipelineBase* MakeError(DeviceBase* device); static ComputePipelineBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
// Functors necessary for the unordered_set<ComputePipelineBase*>-based cache. // Functors necessary for the unordered_set<ComputePipelineBase*>-based cache.
@ -45,6 +44,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
ComputePipelineBase(DeviceBase* device); ComputePipelineBase(DeviceBase* device);
void DestroyImpl() override;
private: private:
ComputePipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag); ComputePipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);

View File

@ -122,9 +122,8 @@ namespace dawn_native {
Destroy(); Destroy();
} }
bool ExternalTextureBase::Destroy() { void ExternalTextureBase::DestroyImpl() {
mState = ExternalTextureState::Destroyed; mState = ExternalTextureState::Destroyed;
return ApiObjectBase::Destroy();
} }
// static // static

View File

@ -42,7 +42,6 @@ namespace dawn_native {
static ExternalTextureBase* MakeError(DeviceBase* device); static ExternalTextureBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
void APIDestroy(); void APIDestroy();
@ -50,6 +49,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
ExternalTextureBase(DeviceBase* device); ExternalTextureBase(DeviceBase* device);
void DestroyImpl() override;
private: private:
enum class ExternalTextureState { Alive, Destroyed }; enum class ExternalTextureState { Alive, Destroyed };

View File

@ -80,20 +80,11 @@ namespace dawn_native {
GetDevice()->TrackObject(this); GetDevice()->TrackObject(this);
} }
bool ApiObjectBase::MarkDestroyed() { void ApiObjectBase::Destroy() {
const std::lock_guard<std::mutex> lock(*GetDevice()->GetObjectListMutex(GetType())); const std::lock_guard<std::mutex> lock(*GetDevice()->GetObjectListMutex(GetType()));
return RemoveFromList(); if (RemoveFromList()) {
}
bool ApiObjectBase::Destroy() {
bool marked = MarkDestroyed();
if (marked) {
DestroyImpl(); DestroyImpl();
} }
return marked;
}
void ApiObjectBase::DestroyImpl() {
} }
} // namespace dawn_native } // namespace dawn_native

View File

@ -60,12 +60,8 @@ namespace dawn_native {
// by the owning device. // by the owning device.
bool IsAlive() const; bool IsAlive() const;
// Allow virtual overriding of actual destroy call in order to allow for re-using of base // This needs to be public because it can be called from the device owning the object.
// destruction oerations. Classes that override this function should almost always call this void Destroy();
// class's implementation in the override. This needs to be public because it can be called
// from the device owning the object. Returns true iff destruction occurs. Upon any re-calls
// of the function it will return false to indicate no further operations should be taken.
virtual bool Destroy();
// Dawn API // Dawn API
void APISetLabel(const char* label); void APISetLabel(const char* label);
@ -84,12 +80,11 @@ namespace dawn_native {
void DeleteThis() override; void DeleteThis() override;
void TrackInDevice(); void TrackInDevice();
// Thread-safe manner to mark an object as destroyed. Returns true iff the call was the // Sub-classes may override this function multiple times. Whenever overriding this function,
// "winning" attempt to destroy the object. This is useful when sub-classes may have extra // however, users should be sure to call their parent's version in the new override to make
// pre-destruction steps that need to occur only once, i.e. Buffer needs to be unmapped // sure that all destroy functionality is kept. This function is guaranteed to only be
// before being destroyed. // called once through the exposed Destroy function.
bool MarkDestroyed(); virtual void DestroyImpl() = 0;
virtual void DestroyImpl();
private: private:
virtual void SetLabelImpl(); virtual void SetLabelImpl();

View File

@ -85,13 +85,11 @@ namespace dawn_native {
PipelineLayoutBase::~PipelineLayoutBase() = default; PipelineLayoutBase::~PipelineLayoutBase() = default;
bool PipelineLayoutBase::Destroy() { void PipelineLayoutBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint
GetDevice()->UncachePipelineLayout(this); GetDevice()->UncachePipelineLayout(this);
} }
return wasDestroyed;
} }
// static // static

View File

@ -61,7 +61,6 @@ namespace dawn_native {
DeviceBase* device, DeviceBase* device,
std::vector<StageAndDescriptor> stages); std::vector<StageAndDescriptor> stages);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
const BindGroupLayoutBase* GetBindGroupLayout(BindGroupIndex group) const; const BindGroupLayoutBase* GetBindGroupLayout(BindGroupIndex group) const;
@ -87,6 +86,7 @@ namespace dawn_native {
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
PipelineLayoutBase(DeviceBase* device); PipelineLayoutBase(DeviceBase* device);
PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag); PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag);
void DestroyImpl() override;
BindGroupLayoutArray mBindGroupLayouts; BindGroupLayoutArray mBindGroupLayouts;
BindGroupLayoutMask mMask; BindGroupLayoutMask mMask;

View File

@ -126,9 +126,8 @@ namespace dawn_native {
ASSERT(mState == QuerySetState::Unavailable || mState == QuerySetState::Destroyed); ASSERT(mState == QuerySetState::Unavailable || mState == QuerySetState::Destroyed);
} }
bool QuerySetBase::Destroy() { void QuerySetBase::DestroyImpl() {
mState = QuerySetState::Destroyed; mState = QuerySetState::Destroyed;
return ApiObjectBase::Destroy();
} }
// static // static

View File

@ -31,7 +31,6 @@ namespace dawn_native {
static QuerySetBase* MakeError(DeviceBase* device); static QuerySetBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
wgpu::QueryType GetQueryType() const; wgpu::QueryType GetQueryType() const;
@ -50,6 +49,7 @@ namespace dawn_native {
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
QuerySetBase(DeviceBase* device); QuerySetBase(DeviceBase* device);
void DestroyImpl() override;
~QuerySetBase() override; ~QuerySetBase() override;

View File

@ -173,6 +173,9 @@ namespace dawn_native {
ASSERT(mTasksInFlight.Empty()); ASSERT(mTasksInFlight.Empty());
} }
void QueueBase::DestroyImpl() {
}
// static // static
QueueBase* QueueBase::MakeError(DeviceBase* device) { QueueBase* QueueBase::MakeError(DeviceBase* device) {
return new ErrorQueue(device); return new ErrorQueue(device);

View File

@ -69,6 +69,7 @@ namespace dawn_native {
protected: protected:
QueueBase(DeviceBase* device); QueueBase(DeviceBase* device);
QueueBase(DeviceBase* device, ObjectBase::ErrorTag tag); QueueBase(DeviceBase* device, ObjectBase::ErrorTag tag);
void DestroyImpl() override;
private: private:
MaybeError WriteTextureInternal(const ImageCopyTexture* destination, MaybeError WriteTextureInternal(const ImageCopyTexture* destination,

View File

@ -683,17 +683,15 @@ namespace dawn_native {
RenderPipelineBase::~RenderPipelineBase() = default; RenderPipelineBase::~RenderPipelineBase() = default;
bool RenderPipelineBase::Destroy() { void RenderPipelineBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint or already destroyed.
GetDevice()->UncacheRenderPipeline(this); GetDevice()->UncacheRenderPipeline(this);
} }
// Remove reference to the attachment state so that we don't have lingering references to // Remove reference to the attachment state so that we don't have lingering references to
// it preventing it from being uncached in the device. // it preventing it from being uncached in the device.
mAttachmentState = nullptr; mAttachmentState = nullptr;
return wasDestroyed;
} }
// static // static

View File

@ -63,7 +63,6 @@ namespace dawn_native {
static RenderPipelineBase* MakeError(DeviceBase* device); static RenderPipelineBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
const ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes>& const ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes>&
@ -111,6 +110,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
RenderPipelineBase(DeviceBase* device); RenderPipelineBase(DeviceBase* device);
void DestroyImpl() override;
private: private:
RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag); RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);

View File

@ -100,13 +100,11 @@ namespace dawn_native {
SamplerBase::~SamplerBase() = default; SamplerBase::~SamplerBase() = default;
bool SamplerBase::Destroy() { void SamplerBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint or already destroyed.
GetDevice()->UncacheSampler(this); GetDevice()->UncacheSampler(this);
} }
return wasDestroyed;
} }
// static // static

View File

@ -38,7 +38,6 @@ namespace dawn_native {
static SamplerBase* MakeError(DeviceBase* device); static SamplerBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
bool IsComparison() const; bool IsComparison() const;
@ -58,6 +57,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
SamplerBase(DeviceBase* device); SamplerBase(DeviceBase* device);
void DestroyImpl() override;
private: private:
SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag); SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag);

View File

@ -1204,13 +1204,11 @@ namespace dawn_native {
ShaderModuleBase::~ShaderModuleBase() = default; ShaderModuleBase::~ShaderModuleBase() = default;
bool ShaderModuleBase::Destroy() { void ShaderModuleBase::DestroyImpl() {
bool wasDestroyed = ApiObjectBase::Destroy(); if (IsCachedReference()) {
if (wasDestroyed && IsCachedReference()) { // Do not uncache the actual cached object if we are a blueprint.
// Do not uncache the actual cached object if we are a blueprint or already destroyed.
GetDevice()->UncacheShaderModule(this); GetDevice()->UncacheShaderModule(this);
} }
return wasDestroyed;
} }
// static // static

View File

@ -250,7 +250,6 @@ namespace dawn_native {
static Ref<ShaderModuleBase> MakeError(DeviceBase* device); static Ref<ShaderModuleBase> MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
// Return true iff the program has an entrypoint called `entryPoint`. // Return true iff the program has an entrypoint called `entryPoint`.
@ -279,6 +278,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
ShaderModuleBase(DeviceBase* device); ShaderModuleBase(DeviceBase* device);
void DestroyImpl() override;
MaybeError InitializeBase(ShaderModuleParseResult* parseResult); MaybeError InitializeBase(ShaderModuleParseResult* parseResult);

View File

@ -125,6 +125,9 @@ namespace dawn_native {
SwapChainBase::~SwapChainBase() { SwapChainBase::~SwapChainBase() {
} }
void SwapChainBase::DestroyImpl() {
}
// static // static
SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) { SwapChainBase* SwapChainBase::MakeError(DeviceBase* device) {
return new ErrorSwapChain(device); return new ErrorSwapChain(device);

View File

@ -49,6 +49,7 @@ namespace dawn_native {
protected: protected:
SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag); SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag);
~SwapChainBase() override; ~SwapChainBase() override;
void DestroyImpl() override;
}; };
// The base class for implementation-based SwapChains that are deprecated. // The base class for implementation-based SwapChains that are deprecated.

View File

@ -483,14 +483,8 @@ namespace dawn_native {
: ApiObjectBase(device, tag), mFormat(kUnusedFormat) { : ApiObjectBase(device, tag), mFormat(kUnusedFormat) {
} }
bool TextureBase::Destroy() { void TextureBase::DestroyImpl() {
// We need to run the destroy operations prior to setting the state to destroyed so that
// the state is both consistent, and implementations of the destroy that may check the
// state do not skip operations unintentionally. (Example in Vulkan backend, the destroy
// implementation will not be ran if we are already in the Destroyed state.)
bool wasDestroyed = ApiObjectBase::Destroy();
mState = TextureState::Destroyed; mState = TextureState::Destroyed;
return wasDestroyed;
} }
// static // static
@ -718,6 +712,9 @@ namespace dawn_native {
: ApiObjectBase(device, tag), mFormat(kUnusedFormat) { : ApiObjectBase(device, tag), mFormat(kUnusedFormat) {
} }
void TextureViewBase::DestroyImpl() {
}
// static // static
TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) { TextureViewBase* TextureViewBase::MakeError(DeviceBase* device) {
return new TextureViewBase(device, ObjectBase::kError); return new TextureViewBase(device, ObjectBase::kError);

View File

@ -51,7 +51,6 @@ namespace dawn_native {
static TextureBase* MakeError(DeviceBase* device); static TextureBase* MakeError(DeviceBase* device);
bool Destroy() override;
ObjectType GetType() const override; ObjectType GetType() const override;
wgpu::TextureDimension GetDimension() const; wgpu::TextureDimension GetDimension() const;
@ -99,6 +98,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
TextureBase(DeviceBase* device, TextureState state); TextureBase(DeviceBase* device, TextureState state);
void DestroyImpl() override;
private: private:
TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag); TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag);
@ -140,6 +140,7 @@ namespace dawn_native {
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
TextureViewBase(TextureBase* texture); TextureViewBase(TextureBase* texture);
void DestroyImpl() override;
private: private:
TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag); TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag);

View File

@ -222,6 +222,7 @@ namespace dawn_native { namespace d3d12 {
BindGroup::~BindGroup() = default; BindGroup::~BindGroup() = default;
void BindGroup::DestroyImpl() { void BindGroup::DestroyImpl() {
BindGroupBase::DestroyImpl();
ToBackend(GetLayout())->DeallocateBindGroup(this, &mCPUViewAllocation); ToBackend(GetLayout())->DeallocateBindGroup(this, &mCPUViewAllocation);
ASSERT(!mCPUViewAllocation.IsValid()); ASSERT(!mCPUViewAllocation.IsValid());
} }

View File

@ -379,8 +379,14 @@ namespace dawn_native { namespace d3d12 {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
// TODO(crbug.com/dawn/1189) Reintroduce optimization to skip flushing the writes to the GPU if (mMappedData != nullptr) {
// memory when we unmap in destruction case since the buffer will be destroyed anyways. // If the buffer is currently mapped, unmap without flushing the writes to the GPU
// since the buffer cannot be used anymore. UnmapImpl checks mWrittenRange to know
// which parts to flush, so we set it to an empty range to prevent flushes.
mWrittenMappedRange = {0, 0};
}
BufferBase::DestroyImpl();
ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation); ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation);
} }

View File

@ -69,6 +69,7 @@ namespace dawn_native { namespace d3d12 {
ComputePipeline::~ComputePipeline() = default; ComputePipeline::~ComputePipeline() = default;
void ComputePipeline::DestroyImpl() { void ComputePipeline::DestroyImpl() {
ComputePipelineBase::DestroyImpl();
ToBackend(GetDevice())->ReferenceUntilUnused(mPipelineState); ToBackend(GetDevice())->ReferenceUntilUnused(mPipelineState);
} }

View File

@ -63,6 +63,7 @@ namespace dawn_native { namespace d3d12 {
QuerySet::~QuerySet() = default; QuerySet::~QuerySet() = default;
void QuerySet::DestroyImpl() { void QuerySet::DestroyImpl() {
QuerySetBase::DestroyImpl();
ToBackend(GetDevice())->ReferenceUntilUnused(mQueryHeap); ToBackend(GetDevice())->ReferenceUntilUnused(mQueryHeap);
mQueryHeap = nullptr; mQueryHeap = nullptr;
} }

View File

@ -425,6 +425,7 @@ namespace dawn_native { namespace d3d12 {
RenderPipeline::~RenderPipeline() = default; RenderPipeline::~RenderPipeline() = default;
void RenderPipeline::DestroyImpl() { void RenderPipeline::DestroyImpl() {
RenderPipelineBase::DestroyImpl();
ToBackend(GetDevice())->ReferenceUntilUnused(mPipelineState); ToBackend(GetDevice())->ReferenceUntilUnused(mPipelineState);
} }

View File

@ -144,6 +144,7 @@ namespace dawn_native { namespace d3d12 {
SwapChain::~SwapChain() = default; SwapChain::~SwapChain() = default;
void SwapChain::DestroyImpl() { void SwapChain::DestroyImpl() {
SwapChainBase::DestroyImpl();
DetachFromSurface(); DetachFromSurface();
} }

View File

@ -658,6 +658,8 @@ namespace dawn_native { namespace d3d12 {
} }
void Texture::DestroyImpl() { void Texture::DestroyImpl() {
TextureBase::DestroyImpl();
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
// In PIX's D3D12-only mode, there is no way to determine frame boundaries // In PIX's D3D12-only mode, there is no way to determine frame boundaries

View File

@ -25,6 +25,7 @@ namespace dawn_native { namespace metal {
BindGroup::~BindGroup() = default; BindGroup::~BindGroup() = default;
void BindGroup::DestroyImpl() { void BindGroup::DestroyImpl() {
BindGroupBase::DestroyImpl();
ToBackend(GetLayout())->DeallocateBindGroup(this); ToBackend(GetLayout())->DeallocateBindGroup(this);
} }

View File

@ -172,6 +172,7 @@ namespace dawn_native { namespace metal {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
BufferBase::DestroyImpl();
mMtlBuffer = nullptr; mMtlBuffer = nullptr;
} }

View File

@ -124,6 +124,8 @@ namespace dawn_native { namespace metal {
QuerySet::~QuerySet() = default; QuerySet::~QuerySet() = default;
void QuerySet::DestroyImpl() { void QuerySet::DestroyImpl() {
QuerySetBase::DestroyImpl();
mVisibilityBuffer = nullptr; mVisibilityBuffer = nullptr;
// mCounterSampleBuffer isn't an NSRef because API_AVAILABLE doesn't work will with // mCounterSampleBuffer isn't an NSRef because API_AVAILABLE doesn't work will with

View File

@ -76,6 +76,7 @@ namespace dawn_native { namespace metal {
SwapChain::~SwapChain() = default; SwapChain::~SwapChain() = default;
void SwapChain::DestroyImpl() { void SwapChain::DestroyImpl() {
SwapChainBase::DestroyImpl();
DetachFromSurface(); DetachFromSurface();
} }

View File

@ -500,6 +500,7 @@ namespace dawn_native { namespace metal {
} }
void Texture::DestroyImpl() { void Texture::DestroyImpl() {
TextureBase::DestroyImpl();
mMtlTexture = nullptr; mMtlTexture = nullptr;
} }

View File

@ -331,6 +331,7 @@ namespace dawn_native { namespace null {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
BufferBase::DestroyImpl();
ToBackend(GetDevice())->DecrementMemoryUsage(GetSize()); ToBackend(GetDevice())->DecrementMemoryUsage(GetSize());
} }
@ -346,9 +347,6 @@ namespace dawn_native { namespace null {
: QuerySetBase(device, descriptor) { : QuerySetBase(device, descriptor) {
} }
void QuerySet::DestroyImpl() {
}
// Queue // Queue
Queue::Queue(Device* device) : QueueBase(device) { Queue::Queue(Device* device) : QueueBase(device) {

View File

@ -246,9 +246,6 @@ namespace dawn_native { namespace null {
class QuerySet final : public QuerySetBase { class QuerySet final : public QuerySetBase {
public: public:
QuerySet(Device* device, const QuerySetDescriptor* descriptor); QuerySet(Device* device, const QuerySetDescriptor* descriptor);
private:
void DestroyImpl() override;
}; };
class Queue final : public QueueBase { class Queue final : public QueueBase {

View File

@ -53,6 +53,7 @@ namespace dawn_native { namespace opengl {
BindGroup::~BindGroup() = default; BindGroup::~BindGroup() = default;
void BindGroup::DestroyImpl() { void BindGroup::DestroyImpl() {
BindGroupBase::DestroyImpl();
ToBackend(GetLayout())->DeallocateBindGroup(this); ToBackend(GetLayout())->DeallocateBindGroup(this);
} }

View File

@ -175,6 +175,7 @@ namespace dawn_native { namespace opengl {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
BufferBase::DestroyImpl();
ToBackend(GetDevice())->gl.DeleteBuffers(1, &mBuffer); ToBackend(GetDevice())->gl.DeleteBuffers(1, &mBuffer);
mBuffer = 0; mBuffer = 0;
} }

View File

@ -28,6 +28,7 @@ namespace dawn_native { namespace opengl {
ComputePipeline::~ComputePipeline() = default; ComputePipeline::~ComputePipeline() = default;
void ComputePipeline::DestroyImpl() { void ComputePipeline::DestroyImpl() {
ComputePipelineBase::DestroyImpl();
DeleteProgram(ToBackend(GetDevice())->gl); DeleteProgram(ToBackend(GetDevice())->gl);
} }

View File

@ -24,7 +24,4 @@ namespace dawn_native { namespace opengl {
QuerySet::~QuerySet() = default; QuerySet::~QuerySet() = default;
void QuerySet::DestroyImpl() {
}
}} // namespace dawn_native::opengl }} // namespace dawn_native::opengl

View File

@ -27,8 +27,6 @@ namespace dawn_native { namespace opengl {
private: private:
~QuerySet() override; ~QuerySet() override;
void DestroyImpl() override;
}; };
}} // namespace dawn_native::opengl }} // namespace dawn_native::opengl

View File

@ -237,6 +237,7 @@ namespace dawn_native { namespace opengl {
RenderPipeline::~RenderPipeline() = default; RenderPipeline::~RenderPipeline() = default;
void RenderPipeline::DestroyImpl() { void RenderPipeline::DestroyImpl() {
RenderPipelineBase::DestroyImpl();
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
gl.DeleteVertexArrays(1, &mVertexArrayObject); gl.DeleteVertexArrays(1, &mVertexArrayObject);
gl.BindVertexArray(0); gl.BindVertexArray(0);

View File

@ -79,6 +79,7 @@ namespace dawn_native { namespace opengl {
Sampler::~Sampler() = default; Sampler::~Sampler() = default;
void Sampler::DestroyImpl() { void Sampler::DestroyImpl() {
SamplerBase::DestroyImpl();
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
gl.DeleteSamplers(1, &mFilteringHandle); gl.DeleteSamplers(1, &mFilteringHandle);
gl.DeleteSamplers(1, &mNonFilteringHandle); gl.DeleteSamplers(1, &mNonFilteringHandle);

View File

@ -191,6 +191,7 @@ namespace dawn_native { namespace opengl {
} }
void Texture::DestroyImpl() { void Texture::DestroyImpl() {
TextureBase::DestroyImpl();
if (GetTextureState() == TextureState::OwnedInternal) { if (GetTextureState() == TextureState::OwnedInternal) {
ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle); ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle);
mHandle = 0; mHandle = 0;
@ -561,6 +562,7 @@ namespace dawn_native { namespace opengl {
} }
void TextureView::DestroyImpl() { void TextureView::DestroyImpl() {
TextureViewBase::DestroyImpl();
if (mOwnsHandle) { if (mOwnsHandle) {
ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle); ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle);
} }

View File

@ -155,6 +155,8 @@ namespace dawn_native { namespace vulkan {
BindGroupLayout::~BindGroupLayout() = default; BindGroupLayout::~BindGroupLayout() = default;
void BindGroupLayout::DestroyImpl() { void BindGroupLayout::DestroyImpl() {
BindGroupLayoutBase::DestroyImpl();
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
// DescriptorSetLayout aren't used by execution on the GPU and can be deleted at any time, // DescriptorSetLayout aren't used by execution on the GPU and can be deleted at any time,

View File

@ -164,6 +164,7 @@ namespace dawn_native { namespace vulkan {
BindGroup::~BindGroup() = default; BindGroup::~BindGroup() = default;
void BindGroup::DestroyImpl() { void BindGroup::DestroyImpl() {
BindGroupBase::DestroyImpl();
ToBackend(GetLayout())->DeallocateBindGroup(this, &mDescriptorSetAllocation); ToBackend(GetLayout())->DeallocateBindGroup(this, &mDescriptorSetAllocation);
} }

View File

@ -330,6 +330,8 @@ namespace dawn_native { namespace vulkan {
} }
void Buffer::DestroyImpl() { void Buffer::DestroyImpl() {
BufferBase::DestroyImpl();
ToBackend(GetDevice())->GetResourceMemoryAllocator()->Deallocate(&mMemoryAllocation); ToBackend(GetDevice())->GetResourceMemoryAllocator()->Deallocate(&mMemoryAllocation);
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {

View File

@ -92,6 +92,8 @@ namespace dawn_native { namespace vulkan {
ComputePipeline::~ComputePipeline() = default; ComputePipeline::~ComputePipeline() = default;
void ComputePipeline::DestroyImpl() { void ComputePipeline::DestroyImpl() {
ComputePipelineBase::DestroyImpl();
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;

View File

@ -65,6 +65,7 @@ namespace dawn_native { namespace vulkan {
PipelineLayout::~PipelineLayout() = default; PipelineLayout::~PipelineLayout() = default;
void PipelineLayout::DestroyImpl() { void PipelineLayout::DestroyImpl() {
PipelineLayoutBase::DestroyImpl();
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;

View File

@ -102,6 +102,7 @@ namespace dawn_native { namespace vulkan {
QuerySet::~QuerySet() = default; QuerySet::~QuerySet() = default;
void QuerySet::DestroyImpl() { void QuerySet::DestroyImpl() {
QuerySetBase::DestroyImpl();
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;

View File

@ -602,6 +602,7 @@ namespace dawn_native { namespace vulkan {
RenderPipeline::~RenderPipeline() = default; RenderPipeline::~RenderPipeline() = default;
void RenderPipeline::DestroyImpl() { void RenderPipeline::DestroyImpl() {
RenderPipelineBase::DestroyImpl();
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;

View File

@ -112,6 +112,7 @@ namespace dawn_native { namespace vulkan {
Sampler::~Sampler() = default; Sampler::~Sampler() = default;
void Sampler::DestroyImpl() { void Sampler::DestroyImpl() {
SamplerBase::DestroyImpl();
if (mHandle != VK_NULL_HANDLE) { if (mHandle != VK_NULL_HANDLE) {
ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
mHandle = VK_NULL_HANDLE; mHandle = VK_NULL_HANDLE;

View File

@ -100,6 +100,7 @@ namespace dawn_native { namespace vulkan {
} }
void ShaderModule::DestroyImpl() { void ShaderModule::DestroyImpl() {
ShaderModuleBase::DestroyImpl();
// Remove reference to internal cache to trigger cleanup. // Remove reference to internal cache to trigger cleanup.
mTransformedShaderModuleCache = nullptr; mTransformedShaderModuleCache = nullptr;
} }

View File

@ -226,6 +226,7 @@ namespace dawn_native { namespace vulkan {
SwapChain::~SwapChain() = default; SwapChain::~SwapChain() = default;
void SwapChain::DestroyImpl() { void SwapChain::DestroyImpl() {
SwapChainBase::DestroyImpl();
DetachFromSurface(); DetachFromSurface();
} }

View File

@ -833,6 +833,9 @@ namespace dawn_native { namespace vulkan {
// If a signal semaphore exists it should be requested before we delete the texture // If a signal semaphore exists it should be requested before we delete the texture
ASSERT(mSignalSemaphore == VK_NULL_HANDLE); ASSERT(mSignalSemaphore == VK_NULL_HANDLE);
} }
// For Vulkan, we currently run the base destruction code after the internal changes because
// of the dependency on the texture state which the base code overwrites too early.
TextureBase::DestroyImpl();
} }
VkImage Texture::GetHandle() const { VkImage Texture::GetHandle() const {

View File

@ -168,8 +168,8 @@ namespace dawn_native { namespace {
BufferMock bufferMock(&mDevice, BufferBase::BufferState::Mapped); BufferMock bufferMock(&mDevice, BufferBase::BufferState::Mapped);
{ {
InSequence seq; InSequence seq;
EXPECT_CALL(bufferMock, UnmapImpl).Times(1);
EXPECT_CALL(bufferMock, DestroyImpl).Times(1); EXPECT_CALL(bufferMock, DestroyImpl).Times(1);
EXPECT_CALL(bufferMock, UnmapImpl).Times(1);
} }
EXPECT_TRUE(bufferMock.IsAlive()); EXPECT_TRUE(bufferMock.IsAlive());
@ -198,8 +198,8 @@ namespace dawn_native { namespace {
BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Mapped); BufferMock* bufferMock = new BufferMock(&mDevice, BufferBase::BufferState::Mapped);
{ {
InSequence seq; InSequence seq;
EXPECT_CALL(*bufferMock, UnmapImpl).Times(1);
EXPECT_CALL(*bufferMock, DestroyImpl).Times(1); EXPECT_CALL(*bufferMock, DestroyImpl).Times(1);
EXPECT_CALL(*bufferMock, UnmapImpl).Times(1);
} }
{ {
BufferDescriptor desc = {}; BufferDescriptor desc = {};

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class BindGroupLayoutMock final : public BindGroupLayoutBase { class BindGroupLayoutMock final : public BindGroupLayoutBase {
public: public:
BindGroupLayoutMock(DeviceBase* device) : BindGroupLayoutBase(device) { BindGroupLayoutMock(DeviceBase* device) : BindGroupLayoutBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->BindGroupLayoutBase::DestroyImpl();
});
} }
~BindGroupLayoutMock() override = default; ~BindGroupLayoutMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class BindGroupMock : public BindGroupBase { class BindGroupMock : public BindGroupBase {
public: public:
BindGroupMock(DeviceBase* device) : BindGroupBase(device) { BindGroupMock(DeviceBase* device) : BindGroupBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->BindGroupBase::DestroyImpl();
});
} }
~BindGroupMock() override = default; ~BindGroupMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class BufferMock : public BufferBase { class BufferMock : public BufferBase {
public: public:
BufferMock(DeviceBase* device, BufferBase::BufferState state) : BufferBase(device, state) { BufferMock(DeviceBase* device, BufferBase::BufferState state) : BufferBase(device, state) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->BufferBase::DestroyImpl();
});
} }
~BufferMock() override = default; ~BufferMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class CommandBufferMock : public CommandBufferBase { class CommandBufferMock : public CommandBufferBase {
public: public:
CommandBufferMock(DeviceBase* device) : CommandBufferBase(device) { CommandBufferMock(DeviceBase* device) : CommandBufferBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->CommandBufferBase::DestroyImpl();
});
} }
~CommandBufferMock() override = default; ~CommandBufferMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class ComputePipelineMock : public ComputePipelineBase { class ComputePipelineMock : public ComputePipelineBase {
public: public:
ComputePipelineMock(DeviceBase* device) : ComputePipelineBase(device) { ComputePipelineMock(DeviceBase* device) : ComputePipelineBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->ComputePipelineBase::DestroyImpl();
});
} }
~ComputePipelineMock() override = default; ~ComputePipelineMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class ExternalTextureMock : public ExternalTextureBase { class ExternalTextureMock : public ExternalTextureBase {
public: public:
ExternalTextureMock(DeviceBase* device) : ExternalTextureBase(device) { ExternalTextureMock(DeviceBase* device) : ExternalTextureBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->ExternalTextureBase::DestroyImpl();
});
} }
~ExternalTextureMock() override = default; ~ExternalTextureMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class PipelineLayoutMock : public PipelineLayoutBase { class PipelineLayoutMock : public PipelineLayoutBase {
public: public:
PipelineLayoutMock(DeviceBase* device) : PipelineLayoutBase(device) { PipelineLayoutMock(DeviceBase* device) : PipelineLayoutBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->PipelineLayoutBase::DestroyImpl();
});
} }
~PipelineLayoutMock() override = default; ~PipelineLayoutMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class QuerySetMock : public QuerySetBase { class QuerySetMock : public QuerySetBase {
public: public:
QuerySetMock(DeviceBase* device) : QuerySetBase(device) { QuerySetMock(DeviceBase* device) : QuerySetBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->QuerySetBase::DestroyImpl();
});
} }
~QuerySetMock() override = default; ~QuerySetMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class RenderPipelineMock : public RenderPipelineBase { class RenderPipelineMock : public RenderPipelineBase {
public: public:
RenderPipelineMock(DeviceBase* device) : RenderPipelineBase(device) { RenderPipelineMock(DeviceBase* device) : RenderPipelineBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->RenderPipelineBase::DestroyImpl();
});
} }
~RenderPipelineMock() override = default; ~RenderPipelineMock() override = default;

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class SamplerMock : public SamplerBase { class SamplerMock : public SamplerBase {
public: public:
SamplerMock(DeviceBase* device) : SamplerBase(device) { SamplerMock(DeviceBase* device) : SamplerBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->SamplerBase::DestroyImpl();
});
} }
~SamplerMock() override = default; ~SamplerMock() override = default;

View File

@ -16,6 +16,12 @@
namespace dawn_native { namespace dawn_native {
ShaderModuleMock::ShaderModuleMock(DeviceBase* device) : ShaderModuleBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->ShaderModuleBase::DestroyImpl();
});
}
ResultOrError<Ref<ShaderModuleMock>> ShaderModuleMock::Create(DeviceBase* device, ResultOrError<Ref<ShaderModuleMock>> ShaderModuleMock::Create(DeviceBase* device,
const char* source) { const char* source) {
ShaderModuleMock* mock = new ShaderModuleMock(device); ShaderModuleMock* mock = new ShaderModuleMock(device);

View File

@ -27,8 +27,7 @@ namespace dawn_native {
class ShaderModuleMock : public ShaderModuleBase { class ShaderModuleMock : public ShaderModuleBase {
public: public:
ShaderModuleMock(DeviceBase* device) : ShaderModuleBase(device) { ShaderModuleMock(DeviceBase* device);
}
~ShaderModuleMock() override = default; ~ShaderModuleMock() override = default;
MOCK_METHOD(void, DestroyImpl, (), (override)); MOCK_METHOD(void, DestroyImpl, (), (override));

View File

@ -25,6 +25,9 @@ namespace dawn_native {
class SwapChainMock : public SwapChainBase { class SwapChainMock : public SwapChainBase {
public: public:
SwapChainMock(DeviceBase* device) : SwapChainBase(device) { SwapChainMock(DeviceBase* device) : SwapChainBase(device) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->SwapChainBase::DestroyImpl();
});
} }
~SwapChainMock() override = default; ~SwapChainMock() override = default;

View File

@ -26,6 +26,9 @@ namespace dawn_native {
public: public:
TextureMock(DeviceBase* device, TextureBase::TextureState state) TextureMock(DeviceBase* device, TextureBase::TextureState state)
: TextureBase(device, state) { : TextureBase(device, state) {
ON_CALL(*this, DestroyImpl).WillByDefault([this]() {
this->TextureBase::DestroyImpl();
});
} }
~TextureMock() override = default; ~TextureMock() override = default;