Compare commits
1 Commits
exv/varian
...
pf/fix-fee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7244464a3 |
@@ -18,11 +18,16 @@
|
||||
|
||||
#include <private/backend/PlatformFactory.h>
|
||||
|
||||
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
#endif
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
@@ -33,6 +38,25 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
namespace filament {
|
||||
class InternalVulkanPlatform : public VulkanPlatform {
|
||||
public:
|
||||
InternalVulkanPlatform() {
|
||||
mCustomization = {
|
||||
.gpu = { .index = 0 },
|
||||
};
|
||||
}
|
||||
|
||||
virtual VulkanPlatform::Customization getCustomization() const noexcept override {
|
||||
return mCustomization;
|
||||
}
|
||||
private:
|
||||
VulkanPlatform::Customization mCustomization;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef IOS
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
@@ -40,6 +64,11 @@ using namespace filament::backend;
|
||||
using namespace image;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
std::string const SHADER_VULKAN_TAG = "// select(vulkan) ";
|
||||
std::string const SHADER_METAL_TAG = "// select(metal) ";
|
||||
}
|
||||
|
||||
namespace test {
|
||||
|
||||
Backend BackendTest::sBackend = Backend::NOOP;
|
||||
@@ -67,7 +96,14 @@ BackendTest::~BackendTest() {
|
||||
|
||||
void BackendTest::initializeDriver() {
|
||||
auto backend = static_cast<filament::backend::Backend>(sBackend);
|
||||
Platform* platform = PlatformFactory::create(&backend);
|
||||
|
||||
Platform* platform = nullptr;
|
||||
if (backend == filament::backend::Backend::VULKAN) {
|
||||
platform = new InternalVulkanPlatform();
|
||||
} else {
|
||||
platform = PlatformFactory::create(&backend);
|
||||
}
|
||||
|
||||
assert_invariant(static_cast<uint8_t>(backend) == static_cast<uint8_t>(sBackend));
|
||||
Platform::DriverConfig const driverConfig;
|
||||
driver = platform->createDriver(nullptr, driverConfig);
|
||||
@@ -193,6 +229,32 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si
|
||||
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
|
||||
}
|
||||
|
||||
std::string BackendTest::transformShaderText(std::string const& shader) const {
|
||||
std::string replacement;
|
||||
switch (sBackend) {
|
||||
case test::Backend::VULKAN:
|
||||
replacement = SHADER_VULKAN_TAG;
|
||||
break;
|
||||
case test::Backend::METAL:
|
||||
replacement = SHADER_METAL_TAG;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (replacement.size() == 0) {
|
||||
return shader;
|
||||
}
|
||||
std::string replaced = shader;
|
||||
while (true) {
|
||||
size_t pos = replaced.find(replacement);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
replaced.replace(pos, replacement.length(), "");
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
class Environment : public ::testing::Environment {
|
||||
public:
|
||||
virtual void SetUp() override {
|
||||
|
||||
@@ -66,6 +66,8 @@ protected:
|
||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||
filament::backend::Driver& getDriver() { return *driver; }
|
||||
|
||||
std::string transformShaderText(std::string const& shader) const;
|
||||
|
||||
private:
|
||||
|
||||
filament::backend::Driver* driver = nullptr;
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
using namespace image;
|
||||
#endif
|
||||
|
||||
#define DUMP_INTERMEDIATE_SCREENSHOTS 1
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shaders
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -42,6 +44,7 @@ layout(location = 0) in vec4 mesh_position;
|
||||
void main() {
|
||||
// Hack: move and scale triangle so that it covers entire viewport.
|
||||
gl_Position = vec4((mesh_position.xy + 0.5) * 5.0, 0.0, 1.0);
|
||||
// select(vulkan) gl_Position.y = - gl_Position.y;
|
||||
})";
|
||||
|
||||
static std::string fullscreenFs = R"(#version 450 core
|
||||
@@ -50,7 +53,7 @@ layout(location = 0) out vec4 fragColor;
|
||||
|
||||
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
|
||||
// This parameter is ignored for other backends.
|
||||
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||
layout(binding = 0, set = 1) uniform sampler2D test_tex;
|
||||
|
||||
uniform Params {
|
||||
highp float fbWidth;
|
||||
@@ -60,7 +63,18 @@ uniform Params {
|
||||
} params;
|
||||
void main() {
|
||||
vec2 fbsize = vec2(params.fbWidth, params.fbHeight);
|
||||
vec2 uv = (gl_FragCoord.xy + 0.5) / fbsize;
|
||||
float offset = 0;
|
||||
// select(vulkan) offset = -offset;
|
||||
vec2 uv = (gl_FragCoord.xy + offset) / fbsize;
|
||||
// select(vulkan) uv.y = 1.0 - uv.y;
|
||||
vec4 oo = vec4(0, 0, 0, 0);
|
||||
if (uv.x > .5) {
|
||||
oo += vec4(0.5, 0.0, 0.0, 1.0);
|
||||
}
|
||||
if (uv.y > .5) {
|
||||
oo += vec4(0.0, 0.5, 0.0, 1.0);
|
||||
}
|
||||
// fragColor = oo;
|
||||
fragColor = textureLod(test_tex, uv, params.sourceLevel);
|
||||
})";
|
||||
|
||||
@@ -99,23 +113,58 @@ 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;
|
||||
static void readScreenshot(DriverApi& dapi, Handle<HwRenderTarget> renderTargets[kNumLevels],
|
||||
uint32_t target, std::function<void(void*)> output) {
|
||||
Handle<HwRenderTarget> rt = renderTargets[target];
|
||||
size_t const readWidth = kTexWidth >> target;
|
||||
size_t const readHeight = kTexHeight >> target;
|
||||
size_t const size = readWidth * readHeight * 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);
|
||||
struct ReadPixelUserData {
|
||||
std::function<void(void*)> onRead;
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
|
||||
ReadPixelUserData* userdata = new ReadPixelUserData { output };
|
||||
auto cb = [](void* buffer, size_t size, void* user) {
|
||||
ReadPixelUserData* data = (ReadPixelUserData*) user;
|
||||
data->onRead(buffer);
|
||||
free(buffer);
|
||||
delete data;
|
||||
};
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
|
||||
userdata);
|
||||
dapi.readPixels(rt, 0, 0, readWidth, readHeight, std::move(pb));
|
||||
}
|
||||
|
||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> renderTargets[kNumLevels],
|
||||
uint32_t target, uint32_t numFrame, bool downscale, test::Backend backend,
|
||||
std::string fname="") {
|
||||
Handle<HwRenderTarget> rt = renderTargets[target];
|
||||
size_t const readWidth = kTexWidth >> target;
|
||||
size_t const readHeight = kTexHeight >> target;
|
||||
size_t const size = readWidth * readHeight * 4;
|
||||
if (fname.empty()) {
|
||||
std::string backendStr = "gl";
|
||||
if (backend == test::Backend::METAL) {
|
||||
backendStr = "mtl";
|
||||
} else if (backend == test::Backend::VULKAN) {
|
||||
backendStr = "vk";
|
||||
}
|
||||
std::string const down = downscale ? "down" : "up";
|
||||
fname = "feedback_" + backendStr + "_" + std::to_string(numFrame) + "_" + down + "_" +
|
||||
std::to_string(target) + ".png";
|
||||
}
|
||||
utils::slog.e <<"writing: " << fname << utils::io::endl;
|
||||
readScreenshot(dapi, renderTargets, target,
|
||||
[w = readWidth, h = readHeight, size, fname](void* buffer) {
|
||||
uint32_t const* 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(fname.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", fname.c_str());
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: This test needs work to get Metal and OpenGL to agree on results.
|
||||
@@ -131,6 +180,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
auto swapChain = createSwapChain();
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
bool const backendIsVulkan = sBackend == test::Backend::VULKAN;
|
||||
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
@@ -139,10 +190,12 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(transformShaderText(fullscreenVs),
|
||||
transformShaderText(fullscreenFs), sBackend, sIsMobilePlatform, &sib);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
Program::Sampler psamplers[] = {utils::CString("test_tex"), 0};
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers,
|
||||
sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
@@ -167,10 +220,25 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
const size_t size = kTexHeight * kTexWidth * 4;
|
||||
uint8_t* buffer = (uint8_t*) malloc(size);
|
||||
for (int r = 0, i = 0; r < kTexHeight; r++) {
|
||||
int const adjustedR = backendIsVulkan ? r : kTexHeight - r - 1;
|
||||
for (int c = 0; c < kTexWidth; c++, i += 4) {
|
||||
auto hval = 0x1f * c / (kTexWidth - 1);
|
||||
if (hval > 0x05) {
|
||||
hval = 0x1f;
|
||||
} else {
|
||||
hval = 0x00;
|
||||
}
|
||||
auto vval = 0x1f * adjustedR / (kTexHeight - 1);
|
||||
if (vval > 0x05) {
|
||||
vval = 0x1f;
|
||||
} else {
|
||||
vval = 0x00;
|
||||
}
|
||||
buffer[i + 0] = 0x10;
|
||||
buffer[i + 1] = 0x1f * r / (kTexHeight - 1);
|
||||
buffer[i + 1] = 0x1f * adjustedR / (kTexHeight - 1);
|
||||
// buffer[i + 1] = vval;
|
||||
buffer[i + 2] = 0x1f * c / (kTexWidth - 1);
|
||||
// buffer[i + 2] = hval;
|
||||
buffer[i + 3] = 0xf0;
|
||||
}
|
||||
}
|
||||
@@ -179,7 +247,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
|
||||
|
||||
for (int frame = 0; frame < kNumFrames; frame++) {
|
||||
|
||||
// Prep for rendering.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
@@ -191,8 +258,9 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
state.program = program;
|
||||
SamplerGroup samplers(1);
|
||||
SamplerParams sparams = {};
|
||||
sparams.filterMag = SamplerMagFilter::LINEAR;
|
||||
sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
sparams.filterMag = SamplerMagFilter::NEAREST;
|
||||
// sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
sparams.filterMin = SamplerMinFilter::NEAREST;
|
||||
samplers.setSampler(0, { texture, sparams });
|
||||
auto sgroup =
|
||||
api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test"));
|
||||
@@ -207,19 +275,24 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
// Downsample passes
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
state.rasterState.disableBlending();
|
||||
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
|
||||
const uint32_t sourceLevel = targetLevel - 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
for (int targetLevel = 0; targetLevel < kNumLevels; targetLevel++) {
|
||||
if (targetLevel > 0) {
|
||||
const uint32_t sourceLevel = targetLevel - 1;
|
||||
params.viewport.width = kTexWidth >> targetLevel;
|
||||
params.viewport.height = kTexHeight >> targetLevel;
|
||||
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
|
||||
uploadUniforms(getDriverApi(), ubuffer, {
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
#if DUMP_INTERMEDIATE_SCREENSHOTS
|
||||
dumpScreenshot(api, renderTargets, targetLevel, frame, true, sBackend);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Upsample passes
|
||||
@@ -239,16 +312,22 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
#if DUMP_INTERMEDIATE_SCREENSHOTS
|
||||
dumpScreenshot(api, renderTargets, targetLevel, frame, false, sBackend);
|
||||
#endif
|
||||
}
|
||||
|
||||
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
|
||||
getDriverApi().destroySamplerGroup(sgroup);
|
||||
getDriverApi().destroyBufferObject(ubuffer);
|
||||
|
||||
// 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]);
|
||||
dumpScreenshot(api, renderTargets, 0, frame, false, sBackend, "Feedback.png");
|
||||
}
|
||||
|
||||
api.flush();
|
||||
|
||||
Reference in New Issue
Block a user