voice_audio_multi/tests/unittests/test_metal_im2col_tiled.cpp
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

86 lines
4.3 KiB
C++

#include <ggml.h>
#include <ggml-backend.h>
#include <ggml-metal.h>
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <vector>
namespace {
struct Storage {
ggml_context * ctx = ggml_init({2 * 1024 * 1024, nullptr, true});
ggml_backend_buffer_t buffer = nullptr;
~Storage() {
if (buffer) ggml_backend_buffer_free(buffer);
ggml_free(ctx);
}
};
}
int main() {
auto backend = ggml_backend_metal_init();
if (!backend) return 77;
size_t checked = 0;
try {
// Cover partial tiles, offset views, batches, dilation, and production channel widths.
for (int i = 0; i < 56; ++i) for (auto type : {GGML_TYPE_F32, GGML_TYPE_F16}) {
int w=i<48 ? 7+i%5 : 483, h=i<48 ? 5+i%4 : 9;
int c=i<48 ? 1+i%3 : 64*(1+(i-48)%4), n=1+i%2;
int kw=i<48 ? 1+2*(i%3) : 3, kh=1+2*((i/3)%2);
int sx=1+i%2, sy=1+(i/2)%2, dx=1+(i/5)%2, dy=1+(i/7)%2;
int px=kw/2, py=kh/2;
Storage storage;
auto ctx=storage.ctx;
const size_t count=size_t(w)*h*c*n;
auto base=ggml_new_tensor_1d(ctx,GGML_TYPE_F32,count+2);
auto x=ggml_view_4d(ctx,base,w,h,c,n,w*4,w*h*4,w*h*c*4,4);
auto weight=ggml_new_tensor_4d(ctx,type,kw,kh,c,1);
auto old=ggml_im2col(ctx,weight,x,sx,sy,px,py,dx,dy,true,type);
auto tiled=ggml_im2col(ctx,weight,x,sx,sy,px,py,dx,dy,true,type);
ggml_im2col_2d_set_lowering(tiled,GGML_IM2COL_2D_LOWERING_METAL_TILED);
auto graph=ggml_new_graph(ctx);
ggml_set_output(old); ggml_set_output(tiled);
ggml_build_forward_expand(graph,old); ggml_build_forward_expand(graph,tiled);
storage.buffer=ggml_backend_alloc_ctx_tensors(ctx,backend);
if(!storage.buffer) throw std::runtime_error("allocation failed");
std::vector<float> input(count+2);
for (int pattern = 0; pattern < 2; ++pattern) {
for (size_t j = 0; j < input.size(); ++j) {
input[j] = float(int((j * 37 + i + pattern * 71) % 401) - 200) / 8;
}
ggml_backend_tensor_set(base, input.data(), 0, input.size() * sizeof(float));
if (ggml_backend_graph_compute(backend, graph) != GGML_STATUS_SUCCESS) {
throw std::runtime_error("compute failed");
}
std::vector<unsigned char> actual(ggml_nbytes(tiled)), legacy(actual.size());
ggml_backend_tensor_get(tiled, actual.data(), 0, actual.size());
ggml_backend_tensor_get(old, legacy.data(), 0, legacy.size());
if (actual != legacy) throw std::runtime_error("legacy GPU mismatch");
size_t at = 0;
// Independent coordinate-based oracle, not output from either GPU kernel.
for (int bn = 0; bn < n; ++bn) for (int oy = 0; oy < tiled->ne[2]; ++oy)
for (int ox = 0; ox < tiled->ne[1]; ++ox) for (int ch = 0; ch < c; ++ch)
for (int ky = 0; ky < kh; ++ky) for (int kx = 0; kx < kw; ++kx) {
const int iy = oy * sy + ky * dy - py, ix = ox * sx + kx * dx - px;
const float expected = ix >= 0 && ix < w && iy >= 0 && iy < h
? input[1 + ((bn * c + ch) * h + iy) * w + ix] : 0;
if (type == GGML_TYPE_F32) {
float value;
std::memcpy(&value, actual.data() + at * sizeof(value), sizeof(value));
if (value != expected) throw std::runtime_error("scalar F32 mismatch");
} else {
ggml_fp16_t value;
std::memcpy(&value, actual.data() + at * sizeof(value), sizeof(value));
if (value != ggml_fp32_to_fp16(expected)) throw std::runtime_error("scalar F16 mismatch");
}
++at;
}
checked += at;
}
}
std::cout<<"PASS 112 shapes/types, two input patterns, "<<checked<<" exact values; legacy Metal and independent scalar oracle\n";
} catch(const std::exception & e) {
std::cerr<<e.what()<<'\n';ggml_backend_free(backend);return 1;
}
ggml_backend_free(backend);
}