mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-08 21:19:14 +00:00
- Adds a deprecation warning for the incorrect usage. - Adds test to verify that the warning is emitted and that the results are equal. - Fixes all existing tests to use the newer code path. Change-Id: I01bf85137ad1c66966e1aa0259ae03fc5247ba25 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/130320 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Loko Kung <lokokung@google.com> Reviewed-by: Austin Eng <enga@chromium.org>
1081 lines
44 KiB
C++
1081 lines
44 KiB
C++
// Copyright 2021 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "dawn/native/Toggles.h"
|
|
#include "dawn/native/utils/WGPUHelpers.h"
|
|
#include "dawn/tests/DawnNativeTest.h"
|
|
#include "dawn/tests/MockCallback.h"
|
|
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
|
#include "dawn/utils/WGPUHelpers.h"
|
|
#include "dawn/webgpu_cpp.h"
|
|
#include "mocks/BindGroupLayoutMock.h"
|
|
#include "mocks/BindGroupMock.h"
|
|
#include "mocks/BufferMock.h"
|
|
#include "mocks/CommandBufferMock.h"
|
|
#include "mocks/ComputePipelineMock.h"
|
|
#include "mocks/DawnMockTest.h"
|
|
#include "mocks/DeviceMock.h"
|
|
#include "mocks/ExternalTextureMock.h"
|
|
#include "mocks/PipelineLayoutMock.h"
|
|
#include "mocks/QuerySetMock.h"
|
|
#include "mocks/RenderPipelineMock.h"
|
|
#include "mocks/SamplerMock.h"
|
|
#include "mocks/ShaderModuleMock.h"
|
|
#include "mocks/SwapChainMock.h"
|
|
#include "mocks/TextureMock.h"
|
|
|
|
namespace dawn::native {
|
|
namespace {
|
|
|
|
using ::testing::_;
|
|
using ::testing::ByMove;
|
|
using ::testing::InSequence;
|
|
using ::testing::Mock;
|
|
using testing::MockCallback;
|
|
using ::testing::NiceMock;
|
|
using ::testing::Return;
|
|
using ::testing::StrictMock;
|
|
using ::testing::Test;
|
|
|
|
static constexpr std::string_view kComputeShader = R"(
|
|
@compute @workgroup_size(1) fn main() {}
|
|
)";
|
|
|
|
static constexpr std::string_view kVertexShader = R"(
|
|
@vertex fn main() -> @builtin(position) vec4f {
|
|
return vec4f(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
)";
|
|
|
|
static constexpr std::string_view kFragmentShader = R"(
|
|
@fragment fn main() {}
|
|
)";
|
|
|
|
// Stores and scopes a raw mock object ptr expectation. This is particularly useful on objects that
|
|
// are expected to be destroyed at the end of the scope. In most cases, when the validation in this
|
|
// class's destructor is ran, the pointer is probably already freed.
|
|
class ScopedRawPtrExpectation {
|
|
public:
|
|
explicit ScopedRawPtrExpectation(void* ptr) : mPtr(ptr) {}
|
|
~ScopedRawPtrExpectation() { Mock::VerifyAndClearExpectations(mPtr); }
|
|
|
|
private:
|
|
void* mPtr = nullptr;
|
|
};
|
|
|
|
class DestroyObjectTests : public DawnMockTest {
|
|
public:
|
|
DestroyObjectTests() : DawnMockTest() {
|
|
// Skipping validation on descriptors as coverage for validation is already present.
|
|
mDeviceMock->ForceSetToggleForTesting(Toggle::SkipValidation, true);
|
|
}
|
|
};
|
|
|
|
TEST_F(DestroyObjectTests, BindGroupNativeExplicit) {
|
|
BindGroupDescriptor desc = {};
|
|
desc.layout = mDeviceMock->GetEmptyBindGroupLayoutMock();
|
|
desc.entryCount = 0;
|
|
desc.entries = nullptr;
|
|
|
|
Ref<BindGroupMock> bindGroupMock = AcquireRef(new BindGroupMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bindGroupMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(bindGroupMock->IsAlive());
|
|
bindGroupMock->Destroy();
|
|
EXPECT_FALSE(bindGroupMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, BindGroupImplicit) {
|
|
BindGroupDescriptor desc = {};
|
|
desc.layout = mDeviceMock->GetEmptyBindGroupLayoutMock();
|
|
desc.entryCount = 0;
|
|
desc.entries = nullptr;
|
|
|
|
Ref<BindGroupMock> bindGroupMock = AcquireRef(new BindGroupMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bindGroupMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(bindGroupMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateBindGroupImpl)
|
|
.WillOnce(Return(ByMove(std::move(bindGroupMock))));
|
|
wgpu::BindGroup bindGroup = device.CreateBindGroup(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(bindGroup.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, BindGroupLayoutNativeExplicit) {
|
|
// Use an non-empty bind group layout to avoid hitting the internal empty layout in the cache.
|
|
BindGroupLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutEntry> entries;
|
|
entries.push_back(utils::BindingLayoutEntryInitializationHelper(
|
|
0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform));
|
|
desc.entryCount = static_cast<uint32_t>(entries.size());
|
|
desc.entries = entries.data();
|
|
|
|
Ref<BindGroupLayoutMock> bindGroupLayoutMock =
|
|
AcquireRef(new BindGroupLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bindGroupLayoutMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(bindGroupLayoutMock->IsAlive());
|
|
bindGroupLayoutMock->Destroy();
|
|
EXPECT_FALSE(bindGroupLayoutMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, BindGroupLayoutImplicit) {
|
|
// Use an non-empty bind group layout to avoid hitting the internal empty layout in the cache.
|
|
BindGroupLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutEntry> entries;
|
|
entries.push_back(utils::BindingLayoutEntryInitializationHelper(
|
|
0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform));
|
|
desc.entryCount = static_cast<uint32_t>(entries.size());
|
|
desc.entries = entries.data();
|
|
|
|
Ref<BindGroupLayoutMock> bindGroupLayoutMock =
|
|
AcquireRef(new BindGroupLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bindGroupLayoutMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(bindGroupLayoutMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateBindGroupLayoutImpl)
|
|
.WillOnce(Return(ByMove(std::move(bindGroupLayoutMock))));
|
|
wgpu::BindGroupLayout bindGroupLayout = device.CreateBindGroupLayout(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(bindGroupLayout.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(bindGroupLayout.Get())->IsCachedReference());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, BufferNativeExplicit) {
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::Uniform;
|
|
|
|
Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(bufferMock->IsAlive());
|
|
bufferMock->Destroy();
|
|
EXPECT_FALSE(bufferMock->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, BufferApiExplicit) {
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::Uniform;
|
|
|
|
Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
|
|
wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
|
|
buffer.Destroy();
|
|
EXPECT_FALSE(FromAPI(buffer.Get())->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, BufferImplicit) {
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::Uniform;
|
|
|
|
Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(bufferMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
|
|
wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, MappedBufferApiExplicit) {
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::MapRead;
|
|
|
|
StrictMock<MockCallback<wgpu::BufferMapCallback>> cb;
|
|
EXPECT_CALL(cb, Call).Times(1);
|
|
Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(*bufferMock.Get(), MapAsyncImpl).WillOnce([]() -> MaybeError { return {}; });
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*bufferMock.Get(), UnmapImpl).Times(1);
|
|
}
|
|
{
|
|
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
|
|
wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 16, cb.Callback(), cb.MakeUserdata(this));
|
|
device.Tick();
|
|
|
|
EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
|
|
buffer.Destroy();
|
|
EXPECT_FALSE(FromAPI(buffer.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, MappedBufferImplicit) {
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::MapRead;
|
|
|
|
StrictMock<MockCallback<wgpu::BufferMapCallback>> cb;
|
|
EXPECT_CALL(cb, Call).Times(1);
|
|
Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(*bufferMock.Get(), MapAsyncImpl).WillOnce([]() -> MaybeError { return {}; });
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*bufferMock.Get(), UnmapImpl).Times(1);
|
|
}
|
|
{
|
|
ScopedRawPtrExpectation scoped(bufferMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
|
|
wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 16, cb.Callback(), cb.MakeUserdata(this));
|
|
device.Tick();
|
|
|
|
EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, CommandBufferNativeExplicit) {
|
|
CommandEncoderDescriptor commandEncoderDesc = {};
|
|
Ref<CommandEncoder> commandEncoder = CommandEncoder::Create(mDeviceMock, &commandEncoderDesc);
|
|
|
|
CommandBufferDescriptor commandBufferDesc = {};
|
|
|
|
Ref<CommandBufferMock> commandBufferMock =
|
|
AcquireRef(new CommandBufferMock(mDeviceMock, commandEncoder.Get(), &commandBufferDesc));
|
|
EXPECT_CALL(*commandBufferMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(commandBufferMock->IsAlive());
|
|
commandBufferMock->Destroy();
|
|
EXPECT_FALSE(commandBufferMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, CommandBufferImplicit) {
|
|
CommandEncoderDescriptor commandEncoderDesc = {};
|
|
wgpu::CommandEncoder commandEncoder =
|
|
device.CreateCommandEncoder(ToCppAPI(&commandEncoderDesc));
|
|
|
|
CommandBufferDescriptor commandBufferDesc = {};
|
|
|
|
Ref<CommandBufferMock> commandBufferMock = AcquireRef(
|
|
new CommandBufferMock(mDeviceMock, FromAPI(commandEncoder.Get()), &commandBufferDesc));
|
|
EXPECT_CALL(*commandBufferMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(commandBufferMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateCommandBuffer)
|
|
.WillOnce(Return(ByMove(std::move(commandBufferMock))));
|
|
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(ToCppAPI(&commandBufferDesc));
|
|
|
|
EXPECT_TRUE(FromAPI(commandBuffer.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ComputePipelineNativeExplicit) {
|
|
Ref<ShaderModuleMock> csModuleMock =
|
|
ShaderModuleMock::Create(mDeviceMock, kComputeShader.data());
|
|
ComputePipelineDescriptor desc = {};
|
|
desc.compute.module = csModuleMock.Get();
|
|
desc.compute.entryPoint = "main";
|
|
|
|
Ref<ComputePipelineMock> computePipelineMock = ComputePipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*computePipelineMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(computePipelineMock->IsAlive());
|
|
computePipelineMock->Destroy();
|
|
EXPECT_FALSE(computePipelineMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, ComputePipelineImplicit) {
|
|
Ref<ShaderModuleMock> csModuleMock =
|
|
ShaderModuleMock::Create(mDeviceMock, kComputeShader.data());
|
|
ComputePipelineDescriptor desc = {};
|
|
desc.compute.module = csModuleMock.Get();
|
|
desc.compute.entryPoint = "main";
|
|
|
|
// Compute pipelines are initialized during their creation via the device.
|
|
Ref<ComputePipelineMock> computePipelineMock = ComputePipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*computePipelineMock.Get(), Initialize).Times(1);
|
|
EXPECT_CALL(*computePipelineMock.Get(), DestroyImpl).Times(1);
|
|
|
|
{
|
|
ScopedRawPtrExpectation scoped(computePipelineMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateUninitializedComputePipelineImpl)
|
|
.WillOnce(Return(ByMove(std::move(computePipelineMock))));
|
|
wgpu::ComputePipeline computePipeline = device.CreateComputePipeline(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(computePipeline.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(computePipeline.Get())->IsCachedReference());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ExternalTextureNativeExplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor textureViewDesc = {};
|
|
textureViewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new NiceMock<TextureViewMock>(textureMock.Get(), &textureViewDesc));
|
|
|
|
ExternalTextureDescriptor desc = {};
|
|
std::array<float, 12> placeholderConstantArray;
|
|
desc.yuvToRgbConversionMatrix = placeholderConstantArray.data();
|
|
desc.gamutConversionMatrix = placeholderConstantArray.data();
|
|
desc.srcTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.dstTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.visibleSize = {1, 1};
|
|
desc.plane0 = textureViewMock.Get();
|
|
|
|
Ref<ExternalTextureMock> externalTextureMock = ExternalTextureMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*externalTextureMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(externalTextureMock->IsAlive());
|
|
externalTextureMock->Destroy();
|
|
EXPECT_FALSE(externalTextureMock->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ExternalTextureApiExplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor textureViewDesc = {};
|
|
textureViewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new NiceMock<TextureViewMock>(textureMock.Get(), &textureViewDesc));
|
|
|
|
ExternalTextureDescriptor desc = {};
|
|
std::array<float, 12> placeholderConstantArray;
|
|
desc.yuvToRgbConversionMatrix = placeholderConstantArray.data();
|
|
desc.gamutConversionMatrix = placeholderConstantArray.data();
|
|
desc.srcTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.dstTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.visibleSize = {1, 1};
|
|
desc.plane0 = textureViewMock.Get();
|
|
|
|
Ref<ExternalTextureMock> externalTextureMock = ExternalTextureMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*externalTextureMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateExternalTextureImpl)
|
|
.WillOnce(Return(ByMove(std::move(externalTextureMock))));
|
|
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(externalTexture.Get())->IsAlive());
|
|
externalTexture.Destroy();
|
|
EXPECT_FALSE(FromAPI(externalTexture.Get())->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ExternalTextureImplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor textureViewDesc = {};
|
|
textureViewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new NiceMock<TextureViewMock>(textureMock.Get(), &textureViewDesc));
|
|
|
|
ExternalTextureDescriptor desc = {};
|
|
std::array<float, 12> placeholderConstantArray;
|
|
desc.yuvToRgbConversionMatrix = placeholderConstantArray.data();
|
|
desc.gamutConversionMatrix = placeholderConstantArray.data();
|
|
desc.srcTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.dstTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.visibleSize = {1, 1};
|
|
desc.plane0 = textureViewMock.Get();
|
|
|
|
Ref<ExternalTextureMock> externalTextureMock = ExternalTextureMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*externalTextureMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(externalTextureMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateExternalTextureImpl)
|
|
.WillOnce(Return(ByMove(std::move(externalTextureMock))));
|
|
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(externalTexture.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, PipelineLayoutNativeExplicit) {
|
|
PipelineLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutBase*> bindGroupLayouts;
|
|
bindGroupLayouts.push_back(mDeviceMock->GetEmptyBindGroupLayoutMock());
|
|
desc.bindGroupLayoutCount = static_cast<uint32_t>(bindGroupLayouts.size());
|
|
desc.bindGroupLayouts = bindGroupLayouts.data();
|
|
|
|
Ref<PipelineLayoutMock> pipelineLayoutMock =
|
|
AcquireRef(new PipelineLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*pipelineLayoutMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(pipelineLayoutMock->IsAlive());
|
|
pipelineLayoutMock->Destroy();
|
|
EXPECT_FALSE(pipelineLayoutMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, PipelineLayoutImplicit) {
|
|
PipelineLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutBase*> bindGroupLayouts;
|
|
bindGroupLayouts.push_back(mDeviceMock->GetEmptyBindGroupLayoutMock());
|
|
desc.bindGroupLayoutCount = static_cast<uint32_t>(bindGroupLayouts.size());
|
|
desc.bindGroupLayouts = bindGroupLayouts.data();
|
|
|
|
Ref<PipelineLayoutMock> pipelineLayoutMock =
|
|
AcquireRef(new PipelineLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*pipelineLayoutMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(pipelineLayoutMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreatePipelineLayoutImpl)
|
|
.WillOnce(Return(ByMove(std::move(pipelineLayoutMock))));
|
|
wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(pipelineLayout.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(pipelineLayout.Get())->IsCachedReference());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, QuerySetNativeExplicit) {
|
|
QuerySetDescriptor desc = {};
|
|
desc.type = wgpu::QueryType::Occlusion;
|
|
desc.count = 1;
|
|
|
|
Ref<QuerySetMock> querySetMock = AcquireRef(new QuerySetMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*querySetMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(querySetMock->IsAlive());
|
|
querySetMock->Destroy();
|
|
EXPECT_FALSE(querySetMock->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, QuerySetApiExplicit) {
|
|
QuerySetDescriptor desc = {};
|
|
desc.type = wgpu::QueryType::Occlusion;
|
|
desc.count = 1;
|
|
|
|
Ref<QuerySetMock> querySetMock = AcquireRef(new QuerySetMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*querySetMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateQuerySetImpl).WillOnce(Return(ByMove(std::move(querySetMock))));
|
|
wgpu::QuerySet querySet = device.CreateQuerySet(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(querySet.Get())->IsAlive());
|
|
querySet.Destroy();
|
|
EXPECT_FALSE(FromAPI(querySet.Get())->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, QuerySetImplicit) {
|
|
QuerySetDescriptor desc = {};
|
|
desc.type = wgpu::QueryType::Occlusion;
|
|
desc.count = 1;
|
|
|
|
Ref<QuerySetMock> querySetMock = AcquireRef(new QuerySetMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*querySetMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(querySetMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateQuerySetImpl)
|
|
.WillOnce(Return(ByMove(std::move(querySetMock))));
|
|
wgpu::QuerySet querySet = device.CreateQuerySet(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(querySet.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, RenderPipelineNativeExplicit) {
|
|
Ref<ShaderModuleMock> vsModuleMock =
|
|
ShaderModuleMock::Create(mDeviceMock, kVertexShader.data());
|
|
RenderPipelineDescriptor desc = {};
|
|
desc.vertex.module = vsModuleMock.Get();
|
|
desc.vertex.entryPoint = "main";
|
|
|
|
Ref<RenderPipelineMock> renderPipelineMock = RenderPipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*renderPipelineMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(renderPipelineMock->IsAlive());
|
|
renderPipelineMock->Destroy();
|
|
EXPECT_FALSE(renderPipelineMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, RenderPipelineImplicit) {
|
|
Ref<ShaderModuleMock> vsModuleMock =
|
|
ShaderModuleMock::Create(mDeviceMock, kVertexShader.data());
|
|
RenderPipelineDescriptor desc = {};
|
|
desc.vertex.module = vsModuleMock.Get();
|
|
desc.vertex.entryPoint = "main";
|
|
|
|
// Render pipelines are initialized during their creation via the device.
|
|
Ref<RenderPipelineMock> renderPipelineMock = RenderPipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*renderPipelineMock.Get(), Initialize).Times(1);
|
|
EXPECT_CALL(*renderPipelineMock.Get(), DestroyImpl).Times(1);
|
|
|
|
{
|
|
ScopedRawPtrExpectation scoped(renderPipelineMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateUninitializedRenderPipelineImpl)
|
|
.WillOnce(Return(ByMove(std::move(renderPipelineMock))));
|
|
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(renderPipeline.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(renderPipeline.Get())->IsCachedReference());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, SamplerNativeExplicit) {
|
|
SamplerDescriptor desc = {};
|
|
|
|
Ref<SamplerMock> samplerMock = AcquireRef(new SamplerMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*samplerMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(samplerMock->IsAlive());
|
|
samplerMock->Destroy();
|
|
EXPECT_FALSE(samplerMock->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, SamplerImplicit) {
|
|
SamplerDescriptor desc = {};
|
|
|
|
Ref<SamplerMock> samplerMock = AcquireRef(new SamplerMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*samplerMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(samplerMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateSamplerImpl)
|
|
.WillOnce(Return(ByMove(std::move(samplerMock))));
|
|
wgpu::Sampler sampler = device.CreateSampler(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(sampler.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(sampler.Get())->IsCachedReference());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ShaderModuleNativeExplicit) {
|
|
Ref<ShaderModuleMock> shaderModuleMock =
|
|
ShaderModuleMock::Create(mDeviceMock, kVertexShader.data());
|
|
EXPECT_CALL(*shaderModuleMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(shaderModuleMock->IsAlive());
|
|
shaderModuleMock->Destroy();
|
|
EXPECT_FALSE(shaderModuleMock->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, ShaderModuleImplicit) {
|
|
ShaderModuleWGSLDescriptor wgslDesc = {};
|
|
wgslDesc.code = kVertexShader.data();
|
|
ShaderModuleDescriptor desc = {};
|
|
desc.nextInChain = &wgslDesc;
|
|
|
|
Ref<ShaderModuleMock> shaderModuleMock = ShaderModuleMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*shaderModuleMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(shaderModuleMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateShaderModuleImpl)
|
|
.WillOnce(Return(ByMove(std::move(shaderModuleMock))));
|
|
wgpu::ShaderModule shaderModule = device.CreateShaderModule(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(shaderModule.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, TextureNativeExplicit) {
|
|
TextureDescriptor desc = {};
|
|
desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
desc.size.width = 1;
|
|
desc.size.height = 1;
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
Ref<TextureMock> textureMock = AcquireRef(new TextureMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*textureMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(textureMock->IsAlive());
|
|
textureMock->Destroy();
|
|
EXPECT_FALSE(textureMock->IsAlive());
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, TextureApiExplicit) {
|
|
TextureDescriptor desc = {};
|
|
desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
desc.size.width = 1;
|
|
desc.size.height = 1;
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
Ref<TextureMock> textureMock = AcquireRef(new TextureMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*textureMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureImpl).WillOnce(Return(ByMove(std::move(textureMock))));
|
|
wgpu::Texture texture = device.CreateTexture(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(texture.Get())->IsAlive());
|
|
texture.Destroy();
|
|
EXPECT_FALSE(FromAPI(texture.Get())->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, TextureImplicit) {
|
|
TextureDescriptor desc = {};
|
|
desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
desc.size.width = 1;
|
|
desc.size.height = 1;
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
Ref<TextureMock> textureMock = AcquireRef(new TextureMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*textureMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(textureMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureImpl)
|
|
.WillOnce(Return(ByMove(std::move(textureMock))));
|
|
wgpu::Texture texture = device.CreateTexture(ToCppAPI(&desc));
|
|
|
|
EXPECT_TRUE(FromAPI(texture.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, TextureViewNativeExplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor desc = {};
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
{
|
|
// Explicitly destroy the texture view.
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new TextureViewMock(textureMock.Get(), &desc));
|
|
EXPECT_CALL(*textureViewMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(textureViewMock->IsAlive());
|
|
textureViewMock->Destroy();
|
|
EXPECT_FALSE(textureViewMock->IsAlive());
|
|
}
|
|
{
|
|
// Destroying the owning texture should cause the view to be destroyed as well.
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new TextureViewMock(textureMock.Get(), &desc));
|
|
EXPECT_CALL(*textureViewMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_TRUE(textureViewMock->IsAlive());
|
|
textureMock->Destroy();
|
|
EXPECT_FALSE(textureViewMock->IsAlive());
|
|
}
|
|
}
|
|
|
|
TEST_F(DestroyObjectTests, TextureViewApiExplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor viewDesc = {};
|
|
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new TextureViewMock(textureMock.Get(), &viewDesc));
|
|
EXPECT_CALL(*textureViewMock.Get(), DestroyImpl).Times(1);
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureViewImpl(textureMock.Get(), _))
|
|
.WillOnce(Return(ByMove(std::move(textureViewMock))));
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureImpl).WillOnce(Return(ByMove(std::move(textureMock))));
|
|
wgpu::Texture texture = device.CreateTexture(ToCppAPI(&textureDesc));
|
|
wgpu::TextureView textureView = texture.CreateView(ToCppAPI(&viewDesc));
|
|
|
|
EXPECT_TRUE(FromAPI(textureView.Get())->IsAlive());
|
|
texture.Destroy();
|
|
EXPECT_FALSE(FromAPI(textureView.Get())->IsAlive());
|
|
}
|
|
|
|
// If the reference count on API objects reach 0, they should delete themselves. Note that GTest
|
|
// will also complain if there is a memory leak.
|
|
TEST_F(DestroyObjectTests, TextureViewImplicit) {
|
|
TextureDescriptor textureDesc = {};
|
|
textureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
textureDesc.size.width = 1;
|
|
textureDesc.size.height = 1;
|
|
textureDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
Ref<TextureMock> textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &textureDesc));
|
|
|
|
TextureViewDescriptor viewDesc = {};
|
|
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
Ref<TextureViewMock> textureViewMock =
|
|
AcquireRef(new TextureViewMock(textureMock.Get(), &viewDesc));
|
|
EXPECT_CALL(*textureViewMock.Get(), DestroyImpl).Times(1);
|
|
{
|
|
ScopedRawPtrExpectation scoped(textureViewMock.Get());
|
|
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureViewImpl(textureMock.Get(), _))
|
|
.WillOnce(Return(ByMove(std::move(textureViewMock))));
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureImpl)
|
|
.WillOnce(Return(ByMove(std::move(textureMock))));
|
|
wgpu::Texture texture = device.CreateTexture(ToCppAPI(&textureDesc));
|
|
wgpu::TextureView textureView = texture.CreateView(ToCppAPI(&viewDesc));
|
|
|
|
EXPECT_TRUE(FromAPI(textureView.Get())->IsAlive());
|
|
}
|
|
}
|
|
|
|
// Destroying the objects on the device explicitly should result in all created objects being
|
|
// destroyed in order.
|
|
TEST_F(DestroyObjectTests, DestroyObjectsApiExplicit) {
|
|
Ref<BindGroupMock> bindGroupMock;
|
|
wgpu::BindGroup bindGroup;
|
|
{
|
|
BindGroupDescriptor desc = {};
|
|
desc.layout = mDeviceMock->GetEmptyBindGroupLayoutMock();
|
|
desc.entryCount = 0;
|
|
desc.entries = nullptr;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
bindGroupMock = AcquireRef(new BindGroupMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateBindGroupImpl).WillOnce(Return(bindGroupMock));
|
|
bindGroup = device.CreateBindGroup(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<BindGroupLayoutMock> bindGroupLayoutMock;
|
|
wgpu::BindGroupLayout bindGroupLayout;
|
|
{
|
|
// Use an non-empty bind group layout to avoid hitting the internal empty layout in the
|
|
// cache.
|
|
BindGroupLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutEntry> entries;
|
|
entries.push_back(utils::BindingLayoutEntryInitializationHelper(
|
|
0, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform));
|
|
desc.entryCount = static_cast<uint32_t>(entries.size());
|
|
desc.entries = entries.data();
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
bindGroupLayoutMock = AcquireRef(new BindGroupLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateBindGroupLayoutImpl).WillOnce(Return(bindGroupLayoutMock));
|
|
bindGroupLayout = device.CreateBindGroupLayout(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<ShaderModuleMock> csModuleMock;
|
|
wgpu::ShaderModule csModule;
|
|
{
|
|
ShaderModuleWGSLDescriptor wgslDesc = {};
|
|
wgslDesc.code = kComputeShader.data();
|
|
ShaderModuleDescriptor desc = {};
|
|
desc.nextInChain = &wgslDesc;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
csModuleMock = ShaderModuleMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*mDeviceMock, CreateShaderModuleImpl).WillOnce(Return(csModuleMock));
|
|
csModule = device.CreateShaderModule(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<ShaderModuleMock> vsModuleMock;
|
|
wgpu::ShaderModule vsModule;
|
|
{
|
|
ShaderModuleWGSLDescriptor wgslDesc = {};
|
|
wgslDesc.code = kVertexShader.data();
|
|
ShaderModuleDescriptor desc = {};
|
|
desc.nextInChain = &wgslDesc;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
vsModuleMock = ShaderModuleMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*mDeviceMock, CreateShaderModuleImpl).WillOnce(Return(vsModuleMock));
|
|
vsModule = device.CreateShaderModule(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<BufferMock> bufferMock;
|
|
wgpu::Buffer buffer;
|
|
{
|
|
BufferDescriptor desc = {};
|
|
desc.size = 16;
|
|
desc.usage = wgpu::BufferUsage::Uniform;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(bufferMock));
|
|
buffer = device.CreateBuffer(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<CommandBufferMock> commandBufferMock;
|
|
wgpu::CommandBuffer commandBuffer;
|
|
{
|
|
CommandEncoderDescriptor commandEncoderDesc = {};
|
|
wgpu::CommandEncoder commandEncoder =
|
|
device.CreateCommandEncoder(ToCppAPI(&commandEncoderDesc));
|
|
|
|
CommandBufferDescriptor commandBufferDesc = {};
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
commandBufferMock = AcquireRef(
|
|
new CommandBufferMock(mDeviceMock, FromAPI(commandEncoder.Get()), &commandBufferDesc));
|
|
EXPECT_CALL(*mDeviceMock, CreateCommandBuffer).WillOnce(Return(commandBufferMock));
|
|
commandBuffer = commandEncoder.Finish(ToCppAPI(&commandBufferDesc));
|
|
}
|
|
|
|
Ref<ComputePipelineMock> computePipelineMock;
|
|
wgpu::ComputePipeline computePipeline;
|
|
{
|
|
ComputePipelineDescriptor desc = {};
|
|
desc.compute.module = csModuleMock.Get();
|
|
desc.compute.entryPoint = "main";
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
computePipelineMock = ComputePipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*computePipelineMock.Get(), Initialize).Times(1);
|
|
EXPECT_CALL(*mDeviceMock, CreateUninitializedComputePipelineImpl)
|
|
.WillOnce(Return(computePipelineMock));
|
|
computePipeline = device.CreateComputePipeline(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<PipelineLayoutMock> pipelineLayoutMock;
|
|
wgpu::PipelineLayout pipelineLayout;
|
|
{
|
|
PipelineLayoutDescriptor desc = {};
|
|
std::vector<BindGroupLayoutBase*> bindGroupLayouts;
|
|
bindGroupLayouts.push_back(mDeviceMock->GetEmptyBindGroupLayoutMock());
|
|
desc.bindGroupLayoutCount = static_cast<uint32_t>(bindGroupLayouts.size());
|
|
desc.bindGroupLayouts = bindGroupLayouts.data();
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
pipelineLayoutMock = AcquireRef(new PipelineLayoutMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreatePipelineLayoutImpl).WillOnce(Return(pipelineLayoutMock));
|
|
pipelineLayout = device.CreatePipelineLayout(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<QuerySetMock> querySetMock;
|
|
wgpu::QuerySet querySet;
|
|
{
|
|
QuerySetDescriptor desc = {};
|
|
desc.type = wgpu::QueryType::Occlusion;
|
|
desc.count = 1;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
querySetMock = AcquireRef(new QuerySetMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateQuerySetImpl).WillOnce(Return(querySetMock));
|
|
querySet = device.CreateQuerySet(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<RenderPipelineMock> renderPipelineMock;
|
|
wgpu::RenderPipeline renderPipeline;
|
|
{
|
|
RenderPipelineDescriptor desc = {};
|
|
desc.vertex.module = vsModuleMock.Get();
|
|
desc.vertex.entryPoint = "main";
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
renderPipelineMock = RenderPipelineMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*renderPipelineMock.Get(), Initialize).Times(1);
|
|
EXPECT_CALL(*mDeviceMock, CreateUninitializedRenderPipelineImpl)
|
|
.WillOnce(Return(renderPipelineMock));
|
|
renderPipeline = device.CreateRenderPipeline(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<SamplerMock> samplerMock;
|
|
wgpu::Sampler sampler;
|
|
{
|
|
SamplerDescriptor desc = {};
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
samplerMock = AcquireRef(new SamplerMock(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateSamplerImpl).WillOnce(Return(samplerMock));
|
|
sampler = device.CreateSampler(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<TextureMock> textureMock;
|
|
wgpu::Texture texture;
|
|
{
|
|
TextureDescriptor desc = {};
|
|
desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
desc.size.width = 1;
|
|
desc.size.height = 1;
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
textureMock = AcquireRef(new NiceMock<TextureMock>(mDeviceMock, &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureImpl).WillOnce(Return(textureMock));
|
|
texture = device.CreateTexture(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<TextureViewMock> textureViewMock;
|
|
wgpu::TextureView textureView;
|
|
{
|
|
TextureViewDescriptor desc = {};
|
|
desc.format = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
textureViewMock = AcquireRef(new TextureViewMock(textureMock.Get(), &desc));
|
|
EXPECT_CALL(*mDeviceMock, CreateTextureViewImpl).WillOnce(Return(textureViewMock));
|
|
textureView = texture.CreateView(ToCppAPI(&desc));
|
|
}
|
|
|
|
Ref<ExternalTextureMock> externalTextureMock;
|
|
wgpu::ExternalTexture externalTexture;
|
|
{
|
|
ExternalTextureDescriptor desc = {};
|
|
std::array<float, 12> placeholderConstantArray;
|
|
desc.yuvToRgbConversionMatrix = placeholderConstantArray.data();
|
|
desc.gamutConversionMatrix = placeholderConstantArray.data();
|
|
desc.srcTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.dstTransferFunctionParameters = placeholderConstantArray.data();
|
|
desc.visibleSize = {1, 1};
|
|
desc.plane0 = textureViewMock.Get();
|
|
|
|
ScopedRawPtrExpectation scoped(mDeviceMock);
|
|
externalTextureMock = ExternalTextureMock::Create(mDeviceMock, &desc);
|
|
EXPECT_CALL(*mDeviceMock, CreateExternalTextureImpl).WillOnce(Return(externalTextureMock));
|
|
externalTexture = device.CreateExternalTexture(ToCppAPI(&desc));
|
|
}
|
|
|
|
{
|
|
InSequence seq;
|
|
EXPECT_CALL(*commandBufferMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*renderPipelineMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*computePipelineMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*pipelineLayoutMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*bindGroupMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*bindGroupLayoutMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*vsModuleMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*csModuleMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*externalTextureMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*textureMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*textureViewMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*querySetMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*samplerMock.Get(), DestroyImpl).Times(1);
|
|
EXPECT_CALL(*bufferMock.Get(), DestroyImpl).Times(1);
|
|
}
|
|
|
|
EXPECT_TRUE(FromAPI(bindGroup.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(bindGroupLayout.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(commandBuffer.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(computePipeline.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(externalTexture.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(pipelineLayout.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(querySet.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(renderPipeline.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(sampler.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(vsModule.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(csModule.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(texture.Get())->IsAlive());
|
|
EXPECT_TRUE(FromAPI(textureView.Get())->IsAlive());
|
|
device.Destroy();
|
|
EXPECT_FALSE(FromAPI(bindGroup.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(bindGroupLayout.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(buffer.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(commandBuffer.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(computePipeline.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(externalTexture.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(pipelineLayout.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(querySet.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(renderPipeline.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(sampler.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(vsModule.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(csModule.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(texture.Get())->IsAlive());
|
|
EXPECT_FALSE(FromAPI(textureView.Get())->IsAlive());
|
|
}
|
|
|
|
class DestroyObjectRegressionTests : public DawnNativeTest {};
|
|
|
|
// LastRefInCommand* tests are regression test(s) for https://crbug.com/chromium/1318792. The
|
|
// regression tests here are not exhuastive. In order to have an exhuastive test case for this
|
|
// class of failures, we should test every possible command with the commands holding the last
|
|
// references (or as last as possible) of their needed objects. For now, including simple cases
|
|
// including a stripped-down case from the original bug.
|
|
|
|
// Tests that when a RenderPipeline's last reference is held in a command in an unfinished
|
|
// CommandEncoder, that destroying the device still works as expected (and does not cause
|
|
// double-free).
|
|
TEST_F(DestroyObjectRegressionTests, LastRefInCommandRenderPipeline) {
|
|
::utils::BasicRenderPass pass = ::utils::CreateBasicRenderPass(device, 1, 1);
|
|
|
|
::utils::ComboRenderPassDescriptor passDesc{};
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::RenderPassEncoder renderEncoder = encoder.BeginRenderPass(&pass.renderPassInfo);
|
|
|
|
::utils::ComboRenderPipelineDescriptor pipelineDesc;
|
|
pipelineDesc.cTargets[0].writeMask = wgpu::ColorWriteMask::None;
|
|
pipelineDesc.vertex.module = ::utils::CreateShaderModule(device, kVertexShader.data());
|
|
pipelineDesc.vertex.entryPoint = "main";
|
|
pipelineDesc.cFragment.module = ::utils::CreateShaderModule(device, kFragmentShader.data());
|
|
pipelineDesc.cFragment.entryPoint = "main";
|
|
renderEncoder.SetPipeline(device.CreateRenderPipeline(&pipelineDesc));
|
|
|
|
device.Destroy();
|
|
}
|
|
|
|
// Tests that when a ComputePipelines's last reference is held in a command in an unfinished
|
|
// CommandEncoder, that destroying the device still works as expected (and does not cause
|
|
// double-free).
|
|
TEST_F(DestroyObjectRegressionTests, LastRefInCommandComputePipeline) {
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass();
|
|
|
|
wgpu::ComputePipelineDescriptor pipelineDesc;
|
|
pipelineDesc.compute.module = ::utils::CreateShaderModule(device, kComputeShader.data());
|
|
pipelineDesc.compute.entryPoint = "main";
|
|
computeEncoder.SetPipeline(device.CreateComputePipeline(&pipelineDesc));
|
|
|
|
device.Destroy();
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace dawn::native
|