Skip to content

Commit 74a72ed

Browse files
rudybearmeta-codesync[bot]
authored andcommitted
Add comprehensive test coverage for Metal backend APIs
Reviewed By: corporateshark Differential Revision: D94437442 fbshipit-source-id: cfc0f7ff4ca53ecb77a374e0f83745784e44fccf
1 parent db2174d commit 74a72ed

21 files changed

Lines changed: 2770 additions & 0 deletions

src/igl/tests/metal/BindGroup.mm

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
10+
#include "../util/Common.h"
11+
#include "../util/TestDevice.h"
12+
13+
#include <igl/IGL.h>
14+
#include <igl/metal/Device.h>
15+
16+
namespace igl::tests {
17+
18+
//
19+
// MetalBindGroupTest
20+
//
21+
// This test covers bind group creation and destruction on Metal.
22+
//
23+
class MetalBindGroupTest : public ::testing::Test {
24+
public:
25+
MetalBindGroupTest() = default;
26+
~MetalBindGroupTest() override = default;
27+
28+
void SetUp() override {
29+
setDebugBreakEnabled(false);
30+
util::createDeviceAndQueue(device_, cmdQueue_);
31+
ASSERT_NE(device_, nullptr);
32+
}
33+
34+
void TearDown() override {}
35+
36+
protected:
37+
std::shared_ptr<IDevice> device_;
38+
std::shared_ptr<ICommandQueue> cmdQueue_;
39+
};
40+
41+
//
42+
// CreateTextureBindGroup
43+
//
44+
// Test creating a bind group with textures.
45+
//
46+
TEST_F(MetalBindGroupTest, CreateTextureBindGroup) {
47+
Result res;
48+
49+
// Create a texture to put in the bind group
50+
TextureDesc texDesc =
51+
TextureDesc::new2D(TextureFormat::RGBA_UNorm8, 4, 4, TextureDesc::TextureUsageBits::Sampled);
52+
auto texture = device_->createTexture(texDesc, &res);
53+
ASSERT_TRUE(res.isOk()) << res.message;
54+
55+
// Create a sampler
56+
SamplerStateDesc samplerDesc = SamplerStateDesc::newLinear();
57+
auto sampler = device_->createSamplerState(samplerDesc, &res);
58+
ASSERT_TRUE(res.isOk()) << res.message;
59+
60+
BindGroupTextureDesc bgDesc;
61+
bgDesc.textures[0] = texture;
62+
bgDesc.samplers[0] = sampler;
63+
bgDesc.debugName = "testTextureBindGroup";
64+
65+
auto handle = device_->createBindGroup(bgDesc, nullptr, &res);
66+
ASSERT_TRUE(res.isOk()) << res.message;
67+
ASSERT_TRUE(handle.valid());
68+
}
69+
70+
//
71+
// CreateBufferBindGroup
72+
//
73+
// Test creating a bind group with buffers.
74+
//
75+
TEST_F(MetalBindGroupTest, CreateBufferBindGroup) {
76+
Result res;
77+
78+
// Create a buffer
79+
const float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
80+
BufferDesc bufDesc(
81+
BufferDesc::BufferTypeBits::Uniform, data, sizeof(data), ResourceStorage::Shared);
82+
auto buffer = device_->createBuffer(bufDesc, &res);
83+
ASSERT_TRUE(res.isOk()) << res.message;
84+
85+
BindGroupBufferDesc bgDesc;
86+
bgDesc.buffers[0] = std::shared_ptr<IBuffer>(std::move(buffer));
87+
bgDesc.debugName = "testBufferBindGroup";
88+
89+
auto handle = device_->createBindGroup(bgDesc, &res);
90+
ASSERT_TRUE(res.isOk()) << res.message;
91+
ASSERT_TRUE(handle.valid());
92+
}
93+
94+
//
95+
// DestroyBindGroup
96+
//
97+
// Test that destroying a bind group does not crash.
98+
//
99+
TEST_F(MetalBindGroupTest, DestroyBindGroup) {
100+
Result res;
101+
102+
TextureDesc texDesc =
103+
TextureDesc::new2D(TextureFormat::RGBA_UNorm8, 4, 4, TextureDesc::TextureUsageBits::Sampled);
104+
auto texture = device_->createTexture(texDesc, &res);
105+
ASSERT_TRUE(res.isOk()) << res.message;
106+
107+
BindGroupTextureDesc bgDesc;
108+
bgDesc.textures[0] = texture;
109+
bgDesc.debugName = "testDestroyBindGroup";
110+
111+
auto handle = device_->createBindGroup(bgDesc, nullptr, &res);
112+
ASSERT_TRUE(res.isOk()) << res.message;
113+
ASSERT_TRUE(handle.valid());
114+
115+
// Release the handle (transfers ownership) and manually destroy it.
116+
// This should not crash.
117+
BindGroupTextureHandle rawHandle = handle.release();
118+
device_->destroy(rawHandle);
119+
}
120+
121+
} // namespace igl::tests

src/igl/tests/metal/Buffer.mm

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
10+
#include <igl/metal/Buffer.h>
11+
12+
#include "../util/Common.h"
13+
#include "../util/TestDevice.h"
14+
15+
#include <cstring>
16+
#include <igl/IGL.h>
17+
#include <igl/metal/Device.h>
18+
19+
namespace igl::tests {
20+
21+
//
22+
// MetalBufferTest
23+
//
24+
// This test covers igl::metal::Buffer.
25+
// Tests creation, upload, mapping, and storage modes of Metal buffers.
26+
//
27+
class MetalBufferTest : public ::testing::Test {
28+
public:
29+
MetalBufferTest() = default;
30+
~MetalBufferTest() override = default;
31+
32+
void SetUp() override {
33+
setDebugBreakEnabled(false);
34+
util::createDeviceAndQueue(device_, cmdQueue_);
35+
ASSERT_NE(device_, nullptr);
36+
}
37+
38+
void TearDown() override {}
39+
40+
protected:
41+
std::shared_ptr<IDevice> device_;
42+
std::shared_ptr<ICommandQueue> cmdQueue_;
43+
};
44+
45+
//
46+
// CreateBufferWithData
47+
//
48+
// Test creating a buffer with initial data, verify size and content.
49+
//
50+
TEST_F(MetalBufferTest, CreateBufferWithData) {
51+
const float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
52+
const size_t dataSize = sizeof(data);
53+
54+
BufferDesc desc(BufferDesc::BufferTypeBits::Uniform, data, dataSize, ResourceStorage::Shared);
55+
desc.debugName = "testBufferWithData";
56+
57+
Result res;
58+
auto buffer = device_->createBuffer(desc, &res);
59+
ASSERT_TRUE(res.isOk()) << res.message;
60+
ASSERT_NE(buffer, nullptr);
61+
62+
ASSERT_EQ(buffer->getSizeInBytes(), dataSize);
63+
64+
// Map and verify data
65+
Result mapRes;
66+
auto* mapped = static_cast<float*>(buffer->map(BufferRange(dataSize, 0), &mapRes));
67+
ASSERT_TRUE(mapRes.isOk()) << mapRes.message;
68+
ASSERT_NE(mapped, nullptr);
69+
ASSERT_EQ(mapped[0], 1.0f);
70+
ASSERT_EQ(mapped[1], 2.0f);
71+
ASSERT_EQ(mapped[2], 3.0f);
72+
ASSERT_EQ(mapped[3], 4.0f);
73+
buffer->unmap();
74+
}
75+
76+
//
77+
// CreateBufferEmpty
78+
//
79+
// Test creating a buffer without initial data.
80+
//
81+
TEST_F(MetalBufferTest, CreateBufferEmpty) {
82+
const size_t bufferSize = 256;
83+
84+
BufferDesc desc(
85+
BufferDesc::BufferTypeBits::Uniform, nullptr, bufferSize, ResourceStorage::Shared);
86+
desc.debugName = "testBufferEmpty";
87+
88+
Result res;
89+
auto buffer = device_->createBuffer(desc, &res);
90+
ASSERT_TRUE(res.isOk()) << res.message;
91+
ASSERT_NE(buffer, nullptr);
92+
ASSERT_EQ(buffer->getSizeInBytes(), bufferSize);
93+
}
94+
95+
//
96+
// BufferUploadAndVerify
97+
//
98+
// Test uploading data to a buffer, then mapping and verifying the data matches.
99+
//
100+
TEST_F(MetalBufferTest, BufferUploadAndVerify) {
101+
const size_t bufferSize = 4 * sizeof(float);
102+
103+
BufferDesc desc(
104+
BufferDesc::BufferTypeBits::Uniform, nullptr, bufferSize, ResourceStorage::Shared);
105+
desc.debugName = "testBufferUpload";
106+
107+
Result res;
108+
auto buffer = device_->createBuffer(desc, &res);
109+
ASSERT_TRUE(res.isOk()) << res.message;
110+
ASSERT_NE(buffer, nullptr);
111+
112+
const float uploadData[] = {10.0f, 20.0f, 30.0f, 40.0f};
113+
res = buffer->upload(uploadData, BufferRange(sizeof(uploadData), 0));
114+
ASSERT_TRUE(res.isOk()) << res.message;
115+
116+
Result mapRes;
117+
auto* mapped = static_cast<float*>(buffer->map(BufferRange(bufferSize, 0), &mapRes));
118+
ASSERT_TRUE(mapRes.isOk()) << mapRes.message;
119+
ASSERT_NE(mapped, nullptr);
120+
ASSERT_EQ(mapped[0], 10.0f);
121+
ASSERT_EQ(mapped[1], 20.0f);
122+
ASSERT_EQ(mapped[2], 30.0f);
123+
ASSERT_EQ(mapped[3], 40.0f);
124+
buffer->unmap();
125+
}
126+
127+
//
128+
// BufferMapUnmap
129+
//
130+
// Test mapping, writing, unmapping, then re-mapping to verify written data.
131+
//
132+
TEST_F(MetalBufferTest, BufferMapUnmap) {
133+
const size_t bufferSize = 4 * sizeof(float);
134+
135+
BufferDesc desc(
136+
BufferDesc::BufferTypeBits::Uniform, nullptr, bufferSize, ResourceStorage::Shared);
137+
desc.debugName = "testMapUnmap";
138+
139+
Result res;
140+
auto buffer = device_->createBuffer(desc, &res);
141+
ASSERT_TRUE(res.isOk()) << res.message;
142+
ASSERT_NE(buffer, nullptr);
143+
144+
// Map and write data
145+
Result mapRes;
146+
auto* mapped = static_cast<float*>(buffer->map(BufferRange(bufferSize, 0), &mapRes));
147+
ASSERT_TRUE(mapRes.isOk()) << mapRes.message;
148+
ASSERT_NE(mapped, nullptr);
149+
mapped[0] = 100.0f;
150+
mapped[1] = 200.0f;
151+
mapped[2] = 300.0f;
152+
mapped[3] = 400.0f;
153+
buffer->unmap();
154+
155+
// Re-map and verify
156+
auto* mapped2 = static_cast<float*>(buffer->map(BufferRange(bufferSize, 0), &mapRes));
157+
ASSERT_TRUE(mapRes.isOk()) << mapRes.message;
158+
ASSERT_NE(mapped2, nullptr);
159+
ASSERT_EQ(mapped2[0], 100.0f);
160+
ASSERT_EQ(mapped2[1], 200.0f);
161+
ASSERT_EQ(mapped2[2], 300.0f);
162+
ASSERT_EQ(mapped2[3], 400.0f);
163+
buffer->unmap();
164+
}
165+
166+
//
167+
// StorageModeShared
168+
//
169+
// Verify that a buffer created with Shared storage mode reports Shared.
170+
//
171+
TEST_F(MetalBufferTest, StorageModeShared) {
172+
const size_t bufferSize = 64;
173+
174+
BufferDesc desc(
175+
BufferDesc::BufferTypeBits::Uniform, nullptr, bufferSize, ResourceStorage::Shared);
176+
desc.debugName = "testSharedStorage";
177+
178+
Result res;
179+
auto buffer = device_->createBuffer(desc, &res);
180+
ASSERT_TRUE(res.isOk()) << res.message;
181+
ASSERT_NE(buffer, nullptr);
182+
ASSERT_EQ(buffer->storage(), ResourceStorage::Shared);
183+
}
184+
185+
//
186+
// RingBufferCreation
187+
//
188+
// Create a ring buffer and verify it is non-null.
189+
//
190+
TEST_F(MetalBufferTest, RingBufferCreation) {
191+
const size_t bufferSize = 256;
192+
193+
BufferDesc desc(
194+
BufferDesc::BufferTypeBits::Uniform, nullptr, bufferSize, ResourceStorage::Shared);
195+
desc.hint = BufferDesc::BufferAPIHintBits::Ring;
196+
desc.debugName = "testRingBuffer";
197+
198+
Result res;
199+
auto buffer = device_->createBuffer(desc, &res);
200+
ASSERT_TRUE(res.isOk()) << res.message;
201+
ASSERT_NE(buffer, nullptr);
202+
ASSERT_EQ(buffer->getSizeInBytes(), bufferSize);
203+
}
204+
205+
} // namespace igl::tests

0 commit comments

Comments
 (0)