mediapipe/mediapipe/gpu/gpu_buffer_test.cc
Camillo Lugaresi 523d16dffa Make GpuBuffer a shared_ptr to a storage collection
PiperOrigin-RevId: 493519590
2022-12-06 23:56:19 -08:00

255 lines
8.0 KiB
C++

// Copyright 2019 The MediaPipe 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 "mediapipe/gpu/gpu_buffer.h"
#include <utility>
#include "mediapipe/framework/formats/image_format.pb.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/tool/test_util.h"
#include "mediapipe/gpu/gl_texture_buffer.h"
#include "mediapipe/gpu/gl_texture_util.h"
#include "mediapipe/gpu/gpu_buffer_storage_ahwb.h"
#include "mediapipe/gpu/gpu_buffer_storage_image_frame.h"
#include "mediapipe/gpu/gpu_test_base.h"
#include "stb_image.h"
#include "stb_image_write.h"
namespace mediapipe {
namespace {
void FillImageFrameRGBA(ImageFrame& image, uint8 r, uint8 g, uint8 b, uint8 a) {
auto* data = image.MutablePixelData();
for (int y = 0; y < image.Height(); ++y) {
auto* row = data + image.WidthStep() * y;
for (int x = 0; x < image.Width(); ++x) {
auto* pixel = row + x * image.NumberOfChannels();
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
pixel[3] = a;
}
}
}
class GpuBufferTest : public GpuTestBase {};
TEST_F(GpuBufferTest, BasicTest) {
RunInGlContext([this] {
GpuBuffer buffer = gpu_shared_.gpu_buffer_pool.GetBuffer(300, 200);
EXPECT_EQ(buffer.width(), 300);
EXPECT_EQ(buffer.height(), 200);
EXPECT_TRUE(buffer);
EXPECT_FALSE(buffer == nullptr);
GpuBuffer no_buffer;
EXPECT_FALSE(no_buffer);
EXPECT_TRUE(no_buffer == nullptr);
GpuBuffer buffer2 = buffer;
EXPECT_EQ(buffer, buffer);
EXPECT_EQ(buffer, buffer2);
EXPECT_NE(buffer, no_buffer);
buffer = nullptr;
EXPECT_TRUE(buffer == nullptr);
EXPECT_TRUE(buffer == no_buffer);
});
}
TEST_F(GpuBufferTest, GlTextureView) {
GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
EXPECT_EQ(buffer.width(), 300);
EXPECT_EQ(buffer.height(), 200);
EXPECT_TRUE(buffer);
EXPECT_FALSE(buffer == nullptr);
RunInGlContext([&buffer] {
TempGlFramebuffer fb;
auto view = buffer.GetWriteView<GlTextureView>(0);
FillGlTextureRgba(view, 1.0, 0.0, 0.0, 1.0);
glFlush();
});
std::shared_ptr<const ImageFrame> view = buffer.GetReadView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
ImageFrame red(ImageFormat::SRGBA, 300, 200);
FillImageFrameRGBA(red, 255, 0, 0, 255);
EXPECT_TRUE(CompareImageFrames(*view, red, 0.0, 0.0));
MP_EXPECT_OK(SavePngTestOutput(red, "gltv_red_gold"));
MP_EXPECT_OK(SavePngTestOutput(*view, "gltv_red_view"));
}
TEST_F(GpuBufferTest, ImageFrame) {
GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
EXPECT_EQ(buffer.width(), 300);
EXPECT_EQ(buffer.height(), 200);
EXPECT_TRUE(buffer);
EXPECT_FALSE(buffer == nullptr);
{
std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
FillImageFrameRGBA(*view, 255, 0, 0, 255);
}
GpuBuffer buffer2(300, 200, GpuBufferFormat::kBGRA32);
RunInGlContext([&buffer, &buffer2] {
TempGlFramebuffer fb;
auto src = buffer.GetReadView<GlTextureView>(0);
auto dst = buffer2.GetWriteView<GlTextureView>(0);
CopyGlTexture(src, dst);
glFlush();
});
{
std::shared_ptr<const ImageFrame> view = buffer2.GetReadView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
ImageFrame red(ImageFormat::SRGBA, 300, 200);
FillImageFrameRGBA(red, 255, 0, 0, 255);
EXPECT_TRUE(CompareImageFrames(*view, red, 0.0, 0.0));
MP_EXPECT_OK(SavePngTestOutput(red, "if_red_gold"));
MP_EXPECT_OK(SavePngTestOutput(*view, "if_red_view"));
}
}
TEST_F(GpuBufferTest, Overwrite) {
GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
EXPECT_EQ(buffer.width(), 300);
EXPECT_EQ(buffer.height(), 200);
EXPECT_TRUE(buffer);
EXPECT_FALSE(buffer == nullptr);
{
std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
FillImageFrameRGBA(*view, 255, 0, 0, 255);
}
GpuBuffer red_copy(300, 200, GpuBufferFormat::kBGRA32);
RunInGlContext([&buffer, &red_copy] {
TempGlFramebuffer fb;
auto src = buffer.GetReadView<GlTextureView>(0);
auto dst = red_copy.GetWriteView<GlTextureView>(0);
CopyGlTexture(src, dst);
glFlush();
});
{
std::shared_ptr<const ImageFrame> view = red_copy.GetReadView<ImageFrame>();
ImageFrame red(ImageFormat::SRGBA, 300, 200);
FillImageFrameRGBA(red, 255, 0, 0, 255);
EXPECT_TRUE(CompareImageFrames(*view, red, 0.0, 0.0));
MP_EXPECT_OK(SavePngTestOutput(red, "ow_red_gold"));
MP_EXPECT_OK(SavePngTestOutput(*view, "ow_red_view"));
}
{
std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
FillImageFrameRGBA(*view, 0, 255, 0, 255);
}
GpuBuffer green_copy(300, 200, GpuBufferFormat::kBGRA32);
RunInGlContext([&buffer, &green_copy] {
TempGlFramebuffer fb;
auto src = buffer.GetReadView<GlTextureView>(0);
auto dst = green_copy.GetWriteView<GlTextureView>(0);
CopyGlTexture(src, dst);
glFlush();
});
RunInGlContext([&buffer] {
TempGlFramebuffer fb;
auto view = buffer.GetWriteView<GlTextureView>(0);
FillGlTextureRgba(view, 0.0, 0.0, 1.0, 1.0);
glFlush();
});
{
std::shared_ptr<const ImageFrame> view =
green_copy.GetReadView<ImageFrame>();
ImageFrame green(ImageFormat::SRGBA, 300, 200);
FillImageFrameRGBA(green, 0, 255, 0, 255);
EXPECT_TRUE(CompareImageFrames(*view, green, 0.0, 0.0));
MP_EXPECT_OK(SavePngTestOutput(green, "ow_green_gold"));
MP_EXPECT_OK(SavePngTestOutput(*view, "ow_green_view"));
}
{
std::shared_ptr<const ImageFrame> view = buffer.GetReadView<ImageFrame>();
ImageFrame blue(ImageFormat::SRGBA, 300, 200);
FillImageFrameRGBA(blue, 0, 0, 255, 255);
EXPECT_TRUE(CompareImageFrames(*view, blue, 0.0, 0.0));
MP_EXPECT_OK(SavePngTestOutput(blue, "ow_blue_gold"));
MP_EXPECT_OK(SavePngTestOutput(*view, "ow_blue_view"));
}
}
TEST_F(GpuBufferTest, GlTextureViewRetainsWhatItNeeds) {
GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
{
std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
EXPECT_EQ(view->Width(), 300);
EXPECT_EQ(view->Height(), 200);
FillImageFrameRGBA(*view, 255, 0, 0, 255);
}
RunInGlContext([buffer = std::move(buffer)]() mutable {
// This is not a recommended pattern, but let's make sure that we don't
// crash if the buffer is released before the view. The view can hold
// callbacks into its underlying storage.
auto view = buffer.GetReadView<GlTextureView>(0);
buffer = nullptr;
});
// We're really checking that we haven't crashed.
EXPECT_TRUE(true);
}
TEST_F(GpuBufferTest, CopiesShareConversions) {
GpuBuffer buffer(300, 200, GpuBufferFormat::kBGRA32);
{
std::shared_ptr<ImageFrame> view = buffer.GetWriteView<ImageFrame>();
FillImageFrameRGBA(*view, 255, 0, 0, 255);
}
GpuBuffer other_handle = buffer;
RunInGlContext([&buffer] {
TempGlFramebuffer fb;
auto view = buffer.GetReadView<GlTextureView>(0);
});
// Check that other_handle also sees the same GlTextureBuffer as buffer.
// Note that this is deliberately written so that it still passes on platforms
// where we use another storage for GL textures (they will both be null).
// TODO: expose more accessors for testing?
EXPECT_EQ(other_handle.internal_storage<GlTextureBuffer>(),
buffer.internal_storage<GlTextureBuffer>());
}
} // anonymous namespace
} // namespace mediapipe