Compare commits
1 Commits
bjd/comman
...
pf/fix-fee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7244464a3 |
@@ -18,11 +18,16 @@
|
|||||||
|
|
||||||
#include <private/backend/PlatformFactory.h>
|
#include <private/backend/PlatformFactory.h>
|
||||||
|
|
||||||
|
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||||
|
#include <backend/platforms/VulkanPlatform.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "BackendTest.h"
|
#include "BackendTest.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ShaderGenerator.h"
|
#include "ShaderGenerator.h"
|
||||||
#include "TrianglePrimitive.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;
|
||||||
using namespace filament::backend;
|
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
|
#ifndef IOS
|
||||||
#include <imageio/ImageEncoder.h>
|
#include <imageio/ImageEncoder.h>
|
||||||
#include <image/ColorTransform.h>
|
#include <image/ColorTransform.h>
|
||||||
@@ -40,6 +64,11 @@ using namespace filament::backend;
|
|||||||
using namespace image;
|
using namespace image;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string const SHADER_VULKAN_TAG = "// select(vulkan) ";
|
||||||
|
std::string const SHADER_METAL_TAG = "// select(metal) ";
|
||||||
|
}
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
Backend BackendTest::sBackend = Backend::NOOP;
|
Backend BackendTest::sBackend = Backend::NOOP;
|
||||||
@@ -67,7 +96,14 @@ BackendTest::~BackendTest() {
|
|||||||
|
|
||||||
void BackendTest::initializeDriver() {
|
void BackendTest::initializeDriver() {
|
||||||
auto backend = static_cast<filament::backend::Backend>(sBackend);
|
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));
|
assert_invariant(static_cast<uint8_t>(backend) == static_cast<uint8_t>(sBackend));
|
||||||
Platform::DriverConfig const driverConfig;
|
Platform::DriverConfig const driverConfig;
|
||||||
driver = platform->createDriver(nullptr, 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));
|
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 {
|
class Environment : public ::testing::Environment {
|
||||||
public:
|
public:
|
||||||
virtual void SetUp() override {
|
virtual void SetUp() override {
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ protected:
|
|||||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||||
filament::backend::Driver& getDriver() { return *driver; }
|
filament::backend::Driver& getDriver() { return *driver; }
|
||||||
|
|
||||||
|
std::string transformShaderText(std::string const& shader) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
filament::backend::Driver* driver = nullptr;
|
filament::backend::Driver* driver = nullptr;
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
using namespace image;
|
using namespace image;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DUMP_INTERMEDIATE_SCREENSHOTS 1
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Shaders
|
// Shaders
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -42,6 +44,7 @@ layout(location = 0) in vec4 mesh_position;
|
|||||||
void main() {
|
void main() {
|
||||||
// Hack: move and scale triangle so that it covers entire viewport.
|
// 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);
|
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
|
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.
|
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
|
||||||
// This parameter is ignored for other backends.
|
// 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 {
|
uniform Params {
|
||||||
highp float fbWidth;
|
highp float fbWidth;
|
||||||
@@ -60,7 +63,18 @@ uniform Params {
|
|||||||
} params;
|
} params;
|
||||||
void main() {
|
void main() {
|
||||||
vec2 fbsize = vec2(params.fbWidth, params.fbHeight);
|
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);
|
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);
|
dapi.updateBufferObject(ubh, std::move(bd), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
|
static void readScreenshot(DriverApi& dapi, Handle<HwRenderTarget> renderTargets[kNumLevels],
|
||||||
const size_t size = kTexWidth * kTexHeight * 4;
|
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);
|
void* buffer = calloc(1, size);
|
||||||
|
struct ReadPixelUserData {
|
||||||
|
std::function<void(void*)> onRead;
|
||||||
|
};
|
||||||
|
ReadPixelUserData* userdata = new ReadPixelUserData { output };
|
||||||
auto cb = [](void* buffer, size_t size, void* user) {
|
auto cb = [](void* buffer, size_t size, void* user) {
|
||||||
int w = kTexWidth, h = kTexHeight;
|
ReadPixelUserData* data = (ReadPixelUserData*) user;
|
||||||
const uint32_t* texels = (uint32_t*) buffer;
|
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);
|
sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
|
||||||
#ifndef IOS
|
#ifndef IOS
|
||||||
LinearImage image(w, h, 4);
|
LinearImage image(w, h, 4);
|
||||||
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
|
||||||
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
|
std::ofstream pngstrm(fname.c_str(), std::ios::binary | std::ios::trunc);
|
||||||
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
|
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", fname.c_str());
|
||||||
#endif
|
#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.
|
// 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();
|
auto swapChain = createSwapChain();
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
|
|
||||||
|
bool const backendIsVulkan = sBackend == test::Backend::VULKAN;
|
||||||
|
|
||||||
// Create a program.
|
// Create a program.
|
||||||
ProgramHandle program;
|
ProgramHandle program;
|
||||||
{
|
{
|
||||||
@@ -139,10 +190,12 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||||
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||||
.build();
|
.build();
|
||||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
|
ShaderGenerator shaderGen(transformShaderText(fullscreenVs),
|
||||||
|
transformShaderText(fullscreenFs), sBackend, sIsMobilePlatform, &sib);
|
||||||
Program prog = shaderGen.getProgram(api);
|
Program prog = shaderGen.getProgram(api);
|
||||||
Program::Sampler psamplers[] = {utils::CString("test_tex"), 0};
|
Program::Sampler psamplers[] = {utils::CString("test_tex"), 0};
|
||||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers,
|
||||||
|
sizeof(psamplers) / sizeof(psamplers[0]));
|
||||||
prog.uniformBlockBindings({{"params", 1}});
|
prog.uniformBlockBindings({{"params", 1}});
|
||||||
program = api.createProgram(std::move(prog));
|
program = api.createProgram(std::move(prog));
|
||||||
}
|
}
|
||||||
@@ -167,10 +220,25 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
const size_t size = kTexHeight * kTexWidth * 4;
|
const size_t size = kTexHeight * kTexWidth * 4;
|
||||||
uint8_t* buffer = (uint8_t*) malloc(size);
|
uint8_t* buffer = (uint8_t*) malloc(size);
|
||||||
for (int r = 0, i = 0; r < kTexHeight; r++) {
|
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) {
|
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 + 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] = 0x1f * c / (kTexWidth - 1);
|
||||||
|
// buffer[i + 2] = hval;
|
||||||
buffer[i + 3] = 0xf0;
|
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));
|
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
|
||||||
|
|
||||||
for (int frame = 0; frame < kNumFrames; frame++) {
|
for (int frame = 0; frame < kNumFrames; frame++) {
|
||||||
|
|
||||||
// Prep for rendering.
|
// Prep for rendering.
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::NONE;
|
params.flags.clear = TargetBufferFlags::NONE;
|
||||||
@@ -191,8 +258,9 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
state.program = program;
|
state.program = program;
|
||||||
SamplerGroup samplers(1);
|
SamplerGroup samplers(1);
|
||||||
SamplerParams sparams = {};
|
SamplerParams sparams = {};
|
||||||
sparams.filterMag = SamplerMagFilter::LINEAR;
|
sparams.filterMag = SamplerMagFilter::NEAREST;
|
||||||
sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
// sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||||
|
sparams.filterMin = SamplerMinFilter::NEAREST;
|
||||||
samplers.setSampler(0, { texture, sparams });
|
samplers.setSampler(0, { texture, sparams });
|
||||||
auto sgroup =
|
auto sgroup =
|
||||||
api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test"));
|
api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test"));
|
||||||
@@ -207,7 +275,8 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
// Downsample passes
|
// Downsample passes
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
state.rasterState.disableBlending();
|
state.rasterState.disableBlending();
|
||||||
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
|
for (int targetLevel = 0; targetLevel < kNumLevels; targetLevel++) {
|
||||||
|
if (targetLevel > 0) {
|
||||||
const uint32_t sourceLevel = targetLevel - 1;
|
const uint32_t sourceLevel = targetLevel - 1;
|
||||||
params.viewport.width = kTexWidth >> targetLevel;
|
params.viewport.width = kTexWidth >> targetLevel;
|
||||||
params.viewport.height = kTexHeight >> targetLevel;
|
params.viewport.height = kTexHeight >> targetLevel;
|
||||||
@@ -221,6 +290,10 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
#if DUMP_INTERMEDIATE_SCREENSHOTS
|
||||||
|
dumpScreenshot(api, renderTargets, targetLevel, frame, true, sBackend);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Upsample passes
|
// Upsample passes
|
||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||||
@@ -239,16 +312,22 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
|
|
||||||
|
#if DUMP_INTERMEDIATE_SCREENSHOTS
|
||||||
|
dumpScreenshot(api, renderTargets, targetLevel, frame, false, sBackend);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
|
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
|
||||||
|
getDriverApi().destroySamplerGroup(sgroup);
|
||||||
|
getDriverApi().destroyBufferObject(ubuffer);
|
||||||
|
|
||||||
// Read back the render target corresponding to the base level.
|
// Read back the render target corresponding to the base level.
|
||||||
//
|
//
|
||||||
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
||||||
// seems to be un-reliable on some GPU's.
|
// seems to be un-reliable on some GPU's.
|
||||||
if (frame == kNumFrames - 1) {
|
if (frame == kNumFrames - 1) {
|
||||||
dumpScreenshot(api, renderTargets[0]);
|
dumpScreenshot(api, renderTargets, 0, frame, false, sBackend, "Feedback.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
|
|||||||
Reference in New Issue
Block a user