blob: dbf4592833a9a642b0babaaca03f3b37e0e1bb67 [file] [log] [blame]
// Copyright 2024 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <utility>
#include "dawn/native/DawnNative.h"
#include "dawn/native/SharedResourceMemory.h"
#include "dawn/native/dawn_platform_autogen.h"
#include "dawn/tests/unittests/native/mocks/BufferMock.h"
#include "dawn/tests/unittests/native/mocks/DawnMockTest.h"
#include "dawn/tests/unittests/native/mocks/TextureMock.h"
#include "gtest/gtest.h"
namespace dawn::native {
namespace {
using ::testing::ByMove;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::StrEq;
class MemoryDumpMock : public MemoryDump {
public:
MemoryDumpMock() {
ON_CALL(*this, AddScalar)
.WillByDefault(
[this](const char* name, const char* key, const char* units, uint64_t value) {
if (key == MemoryDump::kNameSize && units == MemoryDump::kUnitsBytes) {
mTotalSize += value;
}
});
}
MOCK_METHOD(void,
AddScalar,
(const char* name, const char* key, const char* units, uint64_t value),
(override));
void AddString(const char* name, const char* key, const std::string& value) override {}
uint64_t GetTotalSize() const { return mTotalSize; }
private:
uint64_t mTotalSize = 0;
};
using MemoryInstrumentationTest = DawnMockTest;
TEST_F(MemoryInstrumentationTest, DumpMemoryStatistics) {
MemoryDumpMock memoryDumpMock;
auto textureLabel = [this](const wgpu::Texture& texture) {
return absl::StrFormat("device_%p/texture_%p", device.Get(), texture.Get());
};
auto bufferLabel = [this](const wgpu::Buffer& buffer) {
return absl::StrFormat("device_%p/buffer_%p", device.Get(), buffer.Get());
};
// Create a buffer and destroy it and check that its size is not counted.
constexpr uint64_t kBufferSize = 31;
constexpr wgpu::BufferDescriptor kBufferDesc = {
.usage = wgpu::BufferUsage::Uniform,
.size = kBufferSize,
};
wgpu::Buffer destroyedBuffer = device.CreateBuffer(&kBufferDesc);
EXPECT_TRUE(destroyedBuffer);
destroyedBuffer.Destroy();
// Create a buffer whose allocated size is larger than requested size and check that allocated
// size is counted.
constexpr uint64_t kBufferAllocatedSize = 32;
Ref<BufferMock> bufferMock = AcquireRef(
new NiceMock<BufferMock>(mDeviceMock, FromCppAPI(&kBufferDesc), kBufferAllocatedSize));
EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
wgpu::Buffer buffer = device.CreateBuffer(&kBufferDesc);
EXPECT_CALL(memoryDumpMock, AddScalar(StrEq(bufferLabel(buffer)), MemoryDump::kNameSize,
MemoryDump::kUnitsBytes, kBufferAllocatedSize));
// Create a mip-mapped texture and check that all mip level sizes are counted.
constexpr wgpu::TextureFormat kRG8UnormTextureFormat = wgpu::TextureFormat::RG8Unorm;
const wgpu::TextureDescriptor kMipmappedTextureDesc = {
.usage = wgpu::TextureUsage::RenderAttachment,
.size = {.width = 30, .height = 20, .depthOrArrayLayers = 10},
.format = kRG8UnormTextureFormat,
.mipLevelCount = 5,
.viewFormatCount = 1,
.viewFormats = &kRG8UnormTextureFormat,
};
wgpu::Texture mipmappedTexture = device.CreateTexture(&kMipmappedTextureDesc);
// Byte size of entire mip chain =
// ((level0 width * level0 height) + ... + (levelN width * levelN height)) * bpp * array layers.
constexpr uint64_t kMipmappedTextureSize =
(((30 * 20) + (15 * 10) + (7 * 5) + (3 * 2) + (1 * 1)) * 2) * 10; // 15840
EXPECT_CALL(memoryDumpMock,
AddScalar(StrEq(textureLabel(mipmappedTexture)), MemoryDump::kNameSize,
MemoryDump::kUnitsBytes, kMipmappedTextureSize));
// Create a multi-sampled texture and check that sample count is taken into account.
const wgpu::TextureDescriptor kMultisampleTextureDesc = {
.usage = wgpu::TextureUsage::RenderAttachment,
.size = {.width = 30, .height = 20},
.format = kRG8UnormTextureFormat,
.sampleCount = 4,
.viewFormatCount = 1,
.viewFormats = &kRG8UnormTextureFormat,
};
wgpu::Texture multisampleTexture = device.CreateTexture(&kMultisampleTextureDesc);
// Expected size = width(30) * height(20) * bytes per pixel(2) * sample count(4).
constexpr uint64_t kMultisampleTextureSize = 30 * 20 * 2 * 4;
EXPECT_CALL(memoryDumpMock,
AddScalar(StrEq(textureLabel(multisampleTexture)), MemoryDump::kNameSize,
MemoryDump::kUnitsBytes, kMultisampleTextureSize));
// Create a compressed texture and check that counted size is correct.
mDeviceMock->ForceEnableFeatureForTesting(Feature::TextureCompressionETC2);
constexpr wgpu::TextureFormat kETC2TextureFormat = wgpu::TextureFormat::ETC2RGB8Unorm;
const wgpu::TextureDescriptor kETC2TextureDesc = {
.usage = wgpu::TextureUsage::CopySrc,
.size = {.width = 32, .height = 32},
.format = kETC2TextureFormat,
.viewFormatCount = 1,
.viewFormats = &kETC2TextureFormat,
};
wgpu::Texture etc2Texture = device.CreateTexture(&kETC2TextureDesc);
// Expected size = (width / block width) * (height / block height) * bytes per block.
constexpr uint64_t kETC2TextureSize = (32 / 4) * (32 / 4) * 8;
EXPECT_CALL(memoryDumpMock, AddScalar(StrEq(textureLabel(etc2Texture)), MemoryDump::kNameSize,
MemoryDump::kUnitsBytes, kETC2TextureSize));
// Create a texture and destroy it and check that its size is not counted.
wgpu::Texture destroyedTexture = device.CreateTexture(&kMipmappedTextureDesc);
EXPECT_TRUE(destroyedTexture);
destroyedTexture.Destroy();
// Create a shared resourc memory texture and check that its size is not counted.
constexpr wgpu::TextureFormat kRGBA8UnormTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
const wgpu::TextureDescriptor kSharedTextureDesc = {
.usage = wgpu::TextureUsage::TextureBinding,
.size = {.width = 30, .height = 20},
.format = kRGBA8UnormTextureFormat,
.viewFormatCount = 1,
.viewFormats = &kRGBA8UnormTextureFormat,
};
Ref<TextureMock> textureMock =
AcquireRef(new NiceMock<TextureMock>(mDeviceMock, FromCppAPI(&kSharedTextureDesc)));
textureMock->SetSharedResourceMemoryContentsForTesting(
AcquireRef(new SharedResourceMemoryContents(nullptr)));
EXPECT_CALL(*mDeviceMock, CreateTextureImpl).WillOnce(Return(ByMove(std::move(textureMock))));
wgpu::Texture sharedTexture = device.CreateTexture(&kSharedTextureDesc);
DumpMemoryStatistics(device.Get(), &memoryDumpMock);
EXPECT_EQ(memoryDumpMock.GetTotalSize(), kBufferAllocatedSize + kMipmappedTextureSize +
kMultisampleTextureSize + kETC2TextureSize);
}
} // namespace
} // namespace dawn::native