Compare commits
1 Commits
rc/1.69.4
...
pf/backend
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e32e6cf2a |
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <fstream>
|
||||
@@ -149,50 +150,59 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
|
||||
}
|
||||
|
||||
void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
||||
Handle<HwRenderTarget> rt, uint32_t expectedHash, bool exportScreenshot) {
|
||||
void* buffer = calloc(1, width * height * 4);
|
||||
Handle<HwRenderTarget> rt, uint32_t const expectedHash, bool const exportScreenshot) {
|
||||
auto img = getRenderTargetRGB(rt, width, height);
|
||||
uint32_t hash = computeImageHash(img);
|
||||
|
||||
struct Capture {
|
||||
uint32_t expectedHash;
|
||||
char* name;
|
||||
bool exportScreenshot;
|
||||
size_t width, height;
|
||||
};
|
||||
auto* c = new Capture();
|
||||
c->expectedHash = expectedHash;
|
||||
c->name = strdup(testName);
|
||||
c->exportScreenshot = exportScreenshot;
|
||||
c->width = width;
|
||||
c->height = height;
|
||||
|
||||
PixelBufferDescriptor pbd(buffer, width * height * 4, PixelDataFormat::RGBA, PixelDataType::UBYTE,
|
||||
1, 0, 0, width, [](void* buffer, size_t size, void* user) {
|
||||
auto* c = (Capture*)user;
|
||||
|
||||
// Export a screenshot, if requested.
|
||||
if (c->exportScreenshot) {
|
||||
#ifndef IOS
|
||||
LinearImage image(c->width, c->height, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(c->width, c->height, c->width * 4,
|
||||
(uint8_t*) buffer);
|
||||
const std::string png = std::string(c->name) + ".png";
|
||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
||||
png);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Hash the contents of the buffer and check that they match.
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
||||
ASSERT_EQ(hash, c->expectedHash) << c->name << " failed: hashes do not match." << std::endl;
|
||||
|
||||
free(buffer);
|
||||
free(c->name);
|
||||
free(c);
|
||||
}, (void*)c);
|
||||
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
|
||||
if (exportScreenshot) {
|
||||
writeImage(img, width, height, utils::CString(testName));
|
||||
}
|
||||
ASSERT_EQ(hash, expectedHash) << testName << " failed: hashes do not match." << std::endl;
|
||||
}
|
||||
|
||||
void BackendTest::writeImage(utils::FixedCapacityVector<uint8_t> img, size_t w, size_t h,
|
||||
utils::CString const& fname) {
|
||||
constexpr size_t MAX_FNAME_LEN = 50;
|
||||
constexpr size_t FNAME_EXT_LEN = 4;
|
||||
constexpr size_t TOTAL_LEN = MAX_FNAME_LEN + FNAME_EXT_LEN + 1;
|
||||
char buf[TOTAL_LEN];
|
||||
|
||||
ASSERT_PRECONDITION(fname.size() <= MAX_FNAME_LEN, "File name %s is too long", fname.c_str());
|
||||
|
||||
snprintf(buf, TOTAL_LEN, "%s.png", fname.c_str());
|
||||
|
||||
#ifndef IOS
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, img.data());
|
||||
std::ofstream pngstrm(buf, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> BackendTest::getRenderTarget(
|
||||
Handle<HwRenderTarget> rt, size_t width, size_t height, PixelDataFormat format,
|
||||
PixelDataType dataType) {
|
||||
auto& api = getDriverApi();
|
||||
size_t const size = width * height * 4;
|
||||
utils::FixedCapacityVector<uint8_t> result(size);
|
||||
PixelBufferDescriptor pb(result.data(), size, format, dataType);
|
||||
api.readPixels(rt, 0, 0, width, height, std::move(pb));
|
||||
flushAndWait();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t BackendTest::computeImageHash(utils::FixedCapacityVector<uint8_t> const& img) {
|
||||
return utils::hash::murmur3((uint32_t*) img.data(), img.size() / 4, 0);
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> BackendTest::getRenderTargetRGB(
|
||||
Handle<HwRenderTarget> rt, size_t width, size_t height) {
|
||||
return getRenderTarget(rt, width, height, PixelDataFormat::RGBA,
|
||||
PixelDataType::UBYTE);
|
||||
}
|
||||
|
||||
|
||||
class Environment : public ::testing::Environment {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
|
||||
@@ -67,6 +67,17 @@ protected:
|
||||
filament::backend::Driver& getDriver() { return *driver; }
|
||||
|
||||
private:
|
||||
void writeImage(utils::FixedCapacityVector<uint8_t> img, size_t w, size_t h,
|
||||
utils::CString const& fname);
|
||||
utils::FixedCapacityVector<uint8_t> getRenderTarget(
|
||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, size_t width,
|
||||
size_t height, filament::backend::PixelDataFormat format,
|
||||
filament::backend::PixelDataType dataType);
|
||||
uint32_t computeImageHash(utils::FixedCapacityVector<uint8_t> const& img);
|
||||
|
||||
utils::FixedCapacityVector<uint8_t> getRenderTargetRGB(
|
||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, size_t width,
|
||||
size_t height);
|
||||
|
||||
filament::backend::Driver* driver = nullptr;
|
||||
filament::backend::CommandBufferQueue commandBufferQueue;
|
||||
|
||||
@@ -60,36 +60,6 @@ struct MaterialParams {
|
||||
float4 scale;
|
||||
};
|
||||
|
||||
struct ScreenshotParams {
|
||||
int width;
|
||||
int height;
|
||||
const char* filename;
|
||||
uint32_t pixelHashResult;
|
||||
};
|
||||
|
||||
#ifdef IOS
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
|
||||
#else
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
|
||||
using namespace image;
|
||||
const size_t size = params->width * params->height * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
ScreenshotParams* params = (ScreenshotParams*) user;
|
||||
int w = params->width, h = params->height;
|
||||
const uint32_t* texels = (uint32_t*) buffer;
|
||||
params->pixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
||||
std::ofstream pngstrm(params->filename, std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", params->filename);
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
|
||||
(void*) params);
|
||||
dapi.readPixels(rt, 0, 0, params->width, params->height, std::move(pb));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
|
||||
MaterialParams* tmp = new MaterialParams(params);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
@@ -243,21 +213,12 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
constexpr uint32_t expected = 0x410bdd31;
|
||||
readPixelsAndAssertHash("ColorMagnify", kDstTexWidth, kDstTexHeight, dstRenderTargets[0],
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x410bdd31;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -315,17 +276,15 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xf3d9c53f;
|
||||
readPixelsAndAssertHash("ColorMinify", kDstTexWidth, kDstTexHeight, dstRenderTargets[0],
|
||||
expected, true);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xf3d9c53f;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
@@ -416,17 +375,13 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
SamplerMagFilter::NEAREST);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, &sparams);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xebfac2ef;
|
||||
readPixelsAndAssertHash("ColorResolve", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0xebfac2ef;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyProgram(program);
|
||||
@@ -489,21 +444,12 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
constexpr uint32_t expected = 0x8de7d55b;
|
||||
readPixelsAndAssertHash("Blit2DTextureArray", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x8de7d55b;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -578,28 +524,17 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
//
|
||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
|
||||
// OpenGL and Metal. So disable golden checking for now.
|
||||
// Use the compare tool from ImageMagick to see visual differences:
|
||||
// compare -verbose -metric mae BlitRegion_Metal.png BlitRegion_OpenGL.png difference.png
|
||||
//
|
||||
// const uint32_t expected = 0x74fa34ed;
|
||||
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
// EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
// api.beginFrame(0, 0, 0);
|
||||
// constexpr uint32_t expected = 0x74fa34ed;
|
||||
// readPixelsAndAssertHash("BlitRegion", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
// expected, true);
|
||||
// api.endFrame(0);
|
||||
// flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
@@ -655,23 +590,22 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
// Grab a screenshot.
|
||||
api.beginFrame(0, 0, 0);
|
||||
constexpr uint32_t expected = 0xebfac2ef;
|
||||
readPixelsAndAssertHash("BlitRegionToSwapChain", kDstTexWidth, kDstTexHeight, dstRenderTarget,
|
||||
expected, true);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "BackendTestUtils.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -64,8 +65,6 @@ void main() {
|
||||
fragColor = textureLod(test_tex, uv, params.sourceLevel);
|
||||
})";
|
||||
|
||||
static uint32_t sPixelHashResult = 0;
|
||||
|
||||
// Selecting a NPOT texture size seems to exacerbate the bug seen with Intel GPU's.
|
||||
// Note that Filament uses a higher precision format (R11F_G11F_B10F) but this does not seem
|
||||
// necessary to trigger the bug.
|
||||
@@ -99,30 +98,13 @@ static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, Material
|
||||
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||
}
|
||||
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
||||
const size_t size = kTexWidth * kTexHeight * 4;
|
||||
void* buffer = calloc(1, size);
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
int w = kTexWidth, h = kTexHeight;
|
||||
const uint32_t* texels = (uint32_t*) buffer;
|
||||
sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
||||
#ifndef IOS
|
||||
LinearImage image(w, h, 4);
|
||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
||||
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
|
||||
#endif
|
||||
free(buffer);
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
|
||||
}
|
||||
|
||||
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
||||
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
|
||||
// backend's readPixels does not work correctly with textures that have image data uploaded.
|
||||
TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto& api = getDriverApi();
|
||||
auto& driver = getDriver();
|
||||
uint32_t pixelHashResult = 0;
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
@@ -147,7 +129,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
TrianglePrimitive const triangle(api);
|
||||
|
||||
// Create a texture.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
@@ -211,8 +193,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
const uint32_t sourceLevel = targetLevel - 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
api.setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
@@ -230,8 +212,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
const uint32_t sourceLevel = targetLevel + 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
api.setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
@@ -241,14 +223,16 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
|
||||
api.setMinMaxLevels(texture, 0, 0x7f);
|
||||
|
||||
// Read back the render target corresponding to the base level.
|
||||
//
|
||||
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
||||
// seems to be un-reliable on some GPU's.
|
||||
if (frame == kNumFrames - 1) {
|
||||
dumpScreenshot(api, renderTargets[0]);
|
||||
constexpr uint32_t expected = 0x70695aa1;
|
||||
readPixelsAndAssertHash("feedback", kTexWidth, kTexHeight, renderTargets[0],
|
||||
expected, true);
|
||||
}
|
||||
|
||||
api.flush();
|
||||
@@ -256,7 +240,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.endFrame(0);
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
driver.purge();
|
||||
}
|
||||
|
||||
api.destroyProgram(program);
|
||||
@@ -264,10 +248,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.destroyTexture(texture);
|
||||
for (auto rt : renderTargets) api.destroyRenderTarget(rt);
|
||||
}
|
||||
|
||||
const uint32_t expected = 0x70695aa1;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sPixelHashResult, expected);
|
||||
EXPECT_TRUE(sPixelHashResult == expected);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
Reference in New Issue
Block a user