Compare commits

..
Author SHA1 Message Date
Powei Feng b3fc8e3796 matc: initialize CustomVariable
The default values were not provided via default construction.
Caused matc to crash on Linux.
2024-08-22 13:32:54 -07:00
Mathias Agopian 5e7106b521 custom variables can now have their precision specified
e.g.:

    variables : [
        {
            name : vertex,
            precision : medium,
         }
    ],
2024-08-22 13:08:48 -07:00
Mathias Agopian 30387af61c reenable the SimplificationPass in spirv-opt
Issues with it were fixed (https://github.com/KhronosGroup/SPIRV-Cross/pull/2163),
so it should be safe to reenable. The invalid glsl code seems to be resolved.
2024-08-22 13:08:23 -07:00
Mathias Agopian 1c2ffc9ed4 new screenshot debug option in gltf_viewer
screenshots are saved in the current directory as 
"screenshotXX.ppm" with XX increasing after each
screenshot.
2024-08-20 22:41:49 -07:00
Sungun Park 574518ea74 Cleanup color pass for multiview (#8062) 2024-08-20 21:44:17 +00:00
Ben Doherty d5a274fa25 Add better glShaderSource fix for IMG devices (#8061) 2024-08-20 12:28:13 -07:00
Mathias Agopian 3351db1178 improve FrameSkipper when disregarding skipping
It is legal for the app to draw a frame even when FrameSkipper 
detects that a frame should be skipped. In that case we used to
overwrite the most recent fence, but this wasn't ideal. We now
proceed as if the fence had signaled, i.e. we destroy it and
move all the fences up, creating a new one at the end of the 
delayed array.
2024-08-20 12:07:59 -07:00
Mathias Agopian b70aa43727 depth clamp cannot work with VSM
This is because the shadowmap doesn't store depth values so it doesn't 
work to "flatten" all casters behind de camera to the near plane.
The bulk of shadowing works but the filtering/edges don't always.

Disable depth-clamping when VSM is enabled.
2024-08-20 12:04:01 -07:00
Sungun Park 1f9a11802d Minor tweak (#8053)
Move the common part outside the loop.
2024-08-20 17:13:55 +00:00
22 changed files with 231 additions and 200 deletions
+11 -2
View File
@@ -1308,7 +1308,12 @@ Description
declare a variable called `eyeDirection` you can access it in the fragment shader using
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
interpolant is of type `float4` (`vec4`) in the shaders.
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
interpolant is `highp` in *both* the vertex and fragment shaders.
An alternate syntax can be used to specify both the name and precision of the interpolant.
In this case the specified precision is used as-is in both fragment and vertex stages, in
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
@@ -1320,7 +1325,11 @@ material {
}
],
variables : [
eyeDirection
eyeDirection,
{
name : eyeColor,
precision : medium
}
],
vertexDomain : device,
depthWrite : false,
@@ -541,9 +541,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
bugs->delay_fbo_destruction = true;
// PowerVR seems to have no problem with this (which is good for us)
bugs->allow_read_only_ancillary_feedback_loop = true;
// PowerVR doesn't respect lengths passed to glShaderSource, so concatenate them into a
// single string.
bugs->concatenate_shader_strings = true;
} else if (strstr(renderer, "Apple")) {
// Apple GPU
} else if (strstr(renderer, "Tegra") ||
@@ -316,12 +316,6 @@ public:
// bugs or performance issues.
bool force_feature_level0;
// Some drivers don't respect the length argument of glShaderSource() and (apparently)
// require each shader source string to be null-terminated. This works around the issue by
// concatenating the strings into a single null-terminated string before passing it to
// glShaderSource().
bool concatenate_shader_strings;
} bugs = {};
// state getters -- as needed.
@@ -568,9 +562,6 @@ private:
{ bugs.force_feature_level0,
"force_feature_level0",
""},
{ bugs.concatenate_shader_strings,
"concatenate_shader_strings",
""},
}};
// this is chosen to minimize code size
@@ -597,40 +597,30 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
version = "#version 310 es\n";
}
const std::array<const char*, 5> sources = {
version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
const std::array<GLint, 5> lengths = {
(GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
GLuint const shaderId = glCreateShader(glShaderType);
if (UTILS_UNLIKELY(context.bugs.concatenate_shader_strings)) {
size_t totalSize = 0;
for (size_t i = 0; i < sources.size(); i++) {
totalSize += lengths[i];
}
std::string concatenatedShaderSource;
concatenatedShaderSource.reserve(totalSize);
for (size_t i = 0; i < sources.size(); i++) {
concatenatedShaderSource.append(sources[i], lengths[i]);
}
const GLchar* ptr = concatenatedShaderSource.c_str();
GLint length = concatenatedShaderSource.length();
glShaderSource(shaderId, 1, &ptr, &length);
} else {
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
}
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glCompileShader(shaderId);
+14 -3
View File
@@ -606,7 +606,7 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
}
}
VulkanAttachment depthStencil;
VulkanAttachment depthStencil[2] = {};
if (depth.handle) {
depthStencil[0] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(depth.handle),
@@ -621,8 +621,19 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
attachmentCount++;
}
// The stencil buffer is always assumed to be part of the depth-stencil buffer.
assert_invariant(!stencil.handle || stencil.handle == depth.handle);
if (stencil.handle) {
depthStencil[1] = {
.texture = mResourceAllocator.handle_cast<VulkanTexture*>(stencil.handle),
.level = stencil.level,
.baseViewIndex = stencil.baseViewIndex,
.layerCount = layerCount,
.layer = stencil.layer,
};
UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D();
tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) };
tmax = { std::max(tmax.x, extent.width), std::max(tmax.y, extent.height) };
attachmentCount++;
}
// All attachments must have the same dimensions, which must be greater than or equal to the
// render target dimensions.
@@ -314,7 +314,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
assert_invariant(!mOffscreen);
VkExtent2D const extent = swapChain.getExtent();
mColor[0] = { .texture = swapChain.getCurrentColor() };
mDepthStencil = { .texture = swapChain.getDepth() };
mDepth = { .texture = swapChain.getDepth() };
width = extent.width;
height = extent.height;
}
@@ -323,7 +323,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil, VulkanStagePool& stagePool, uint8_t layerCount)
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
: HwRenderTarget(width, height),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(true),
@@ -332,8 +332,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
mColor[index] = color[index];
}
mDepthStencil = depthStencil[0];
VulkanTexture* depthTexture = (VulkanTexture*) mDepthStencil.texture;
mDepth = depthStencil[0];
VulkanTexture* depthTexture = (VulkanTexture*) mDepth.texture;
if (samples == 1) {
return;
@@ -372,7 +372,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
// There is no need for sidecar depth if the depth texture is already MSAA.
if (depthTexture->samples > 1) {
mMsaaDepthAttachment = mDepthStencil;
mMsaaDepthAttachment = mDepth;
return;
}
@@ -392,7 +392,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
mMsaaDepthAttachment = {
.texture = msTexture,
.level = msLevel,
.layer = mDepthStencil.layer,
.layer = mDepth.layer,
};
}
@@ -418,8 +418,8 @@ VulkanAttachment& VulkanRenderTarget::getMsaaColor(int target) {
return mMsaaAttachments[target];
}
VulkanAttachment& VulkanRenderTarget::getDepthStencil() {
return mDepthStencil;
VulkanAttachment& VulkanRenderTarget::getDepth() {
return mDepth;
}
VulkanAttachment& VulkanRenderTarget::getMsaaDepth() {
+2 -2
View File
@@ -304,7 +304,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil, VulkanStagePool& stagePool, uint8_t layerCount);
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
@@ -326,7 +326,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
private:
VulkanAttachment mColor[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VulkanAttachment mDepthStencil = {};
VulkanAttachment mDepth = {};
VulkanAttachment mMsaaAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VulkanAttachment mMsaaDepthAttachment = {};
const bool mOffscreen : 1;
+25 -18
View File
@@ -16,16 +16,22 @@
#include "FrameSkipper.h"
#include <utils/Log.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(latency - 1) {
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
@@ -41,31 +47,32 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
}
fence = driver.createFence();
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
}
} // namespace filament
+4 -1
View File
@@ -22,6 +22,9 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -60,7 +63,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
size_t mLast;
uint8_t const mLast;
};
} // namespace filament
+1 -5
View File
@@ -75,7 +75,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
uint8_t layerCount = 1;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
@@ -172,9 +171,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
if (view.hasStereo() && engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
layerCount = engine.getConfig().stereoscopicEyeCount;
}
/*
* There is a bit of magic happening here regarding the viewport used.
@@ -196,7 +192,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.layerCount = layerCount,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},
+13 -2
View File
@@ -371,7 +371,13 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
if (view.isFrontFaceWindingInverted()) {
renderPassFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
}
if (mIsDepthClampSupported && engine.debug.shadowmap.depth_clamp) {
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
@@ -650,9 +656,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
mIsDepthClampSupported && engine.debug.shadowmap.depth_clamp);
canUseDepthClamp);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
+3 -3
View File
@@ -190,10 +190,10 @@ void RenderPassNode::resolve() noexcept {
minHeight = std::min(minHeight, h);
maxHeight = std::max(maxHeight, h);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
assert_invariant(minWidth == maxWidth);
assert_invariant(minHeight == maxHeight);
+10 -1
View File
@@ -323,6 +323,9 @@ public:
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
MaterialBuilder& variable(Variable v, const char* name,
ParameterPrecision precision) noexcept;
/**
* Require a specified attribute.
*
@@ -708,11 +711,17 @@ public:
ShaderStage stage;
};
struct CustomVariable {
utils::CString name;
Precision precision = Precision::DEFAULT;
bool hasPrecision = false;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
using VariableList = CustomVariable[MATERIAL_VARIABLES_COUNT];
using OutputList = std::vector<Output>;
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
+9 -7
View File
@@ -671,13 +671,15 @@ void GLSLPostProcessor::fixupClipDistance(
// - triggers a crash on some Adreno drivers (b/291140208, b/289401984, b/289393290)
// However Metal requires this pass in order to correctly generate half-precision MSL
//
// CreateSimplificationPass() creates a lot of problems:
// Note: CreateSimplificationPass() used to creates a lot of problems:
// - Adreno GPU show artifacts after running simplification passes (Vulkan)
// - spirv-cross fails generating working glsl
// (https://github.com/KhronosGroup/SPIRV-Cross/issues/2162)
// - generally it makes the code more complicated, e.g.: replacing for loops with
// while-if-break, unclear if it helps for anything.
// However, the simplification passes below are necessary when targeting Metal, otherwise the
//
// However this problem was addressed in spirv-cross here:
// https://github.com/KhronosGroup/SPIRV-Cross/pull/2163
//
// The simplification passes below are necessary when targeting Metal, otherwise the
// result is mismatched half / float assignments in MSL.
@@ -710,11 +712,11 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreateRedundancyEliminationPass());
RegisterPass(CreateCombineAccessChainsPass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateVectorDCEPass());
RegisterPass(CreateDeadInsertElimPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateSimplificationPass());
RegisterPass(CreateIfConversionPass());
RegisterPass(CreateCopyPropagateArraysPass());
RegisterPass(CreateReduceLoadSizePass());
@@ -723,7 +725,7 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
RegisterPass(CreateRedundancyEliminationPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateBlockMergePass());
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateSimplificationPass());
}
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& config) {
+16 -2
View File
@@ -234,7 +234,21 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = CString(name);
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
break;
}
return *this;
}
MaterialBuilder& MaterialBuilder::variable(Variable v,
const char* name, ParameterPrecision precision) noexcept {
switch (v) {
case Variable::CUSTOM0:
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), precision, true };
break;
}
return *this;
@@ -1383,7 +1397,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
bool MaterialBuilder::hasCustomVaryings() const noexcept {
for (const auto& variable : mVariables) {
if (!variable.empty()) {
if (!variable.name.empty()) {
return true;
}
}
+9 -4
View File
@@ -451,15 +451,20 @@ io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderStag
}
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderStage stage,
const CString& name, size_t index) {
const MaterialBuilder::CustomVariable& variable, size_t index) {
auto const& name = variable.name;
const char* precisionString = getPrecisionQualifier(variable.precision);
if (!name.empty()) {
if (stage == ShaderStage::VERTEX) {
out << "\n#define VARIABLE_CUSTOM" << index << " " << name.c_str() << "\n";
out << "\n#define VARIABLE_CUSTOM_AT" << index << " variable_" << name.c_str() << "\n";
out << "LAYOUT_LOCATION(" << index << ") VARYING vec4 variable_" << name.c_str() << ";\n";
out << "LAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
} else if (stage == ShaderStage::FRAGMENT) {
out << "\nLAYOUT_LOCATION(" << index << ") VARYING highp vec4 variable_" << name.c_str() << ";\n";
if (!variable.hasPrecision && variable.precision == Precision::DEFAULT) {
// for backward compatibility
precisionString = "highp";
}
out << "\nLAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
}
}
return out;
+1 -1
View File
@@ -103,7 +103,7 @@ public:
// generate declarations for custom interpolants
static utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderStage stage,
const utils::CString& name, size_t index);
const MaterialBuilder::CustomVariable& variable, size_t index);
// generate declarations for non-custom "in" variables
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderStage type,
@@ -224,6 +224,9 @@ public:
*/
static void exportSettings(const Settings& settings, const char* filename);
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine);
Options getOptions() const { return mOptions; }
bool isRunning() const { return mIsRunning; }
size_t currentTest() const { return mCurrentTest; }
+3 -2
View File
@@ -56,7 +56,7 @@ static void convertRGBAtoRGB(void* buffer, uint32_t width, uint32_t height) {
}
}
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
void AutomationEngine::exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine) {
const Viewport& vp = view->getViewport();
const size_t byteCount = vp.width * vp.height * 4;
@@ -244,7 +244,8 @@ void AutomationEngine::tick(Engine* engine, const ViewerContent& content, float
}
if (mOptions.exportScreenshots) {
exportScreenshot(content.view, content.renderer, prefix + ".ppm", isLastTest, this);
AutomationEngine::exportScreenshot(
content.view, content.renderer, prefix + ".ppm", isLastTest, this);
}
if (isLastTest) {
+21 -4
View File
@@ -61,8 +61,10 @@
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include "generated/resources/gltf_demo.h"
@@ -137,6 +139,8 @@ struct App {
AutomationSpec* automationSpec = nullptr;
AutomationEngine* automationEngine = nullptr;
bool screenshot = false;
uint8_t screenshotSeq = 0;
};
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
@@ -877,10 +881,15 @@ int main(int argc, char** argv) {
if (ImGui::CollapsingHeader("Debug")) {
auto& debug = engine->getDebugRegistry();
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
if (engine->getBackend() == Engine::Backend::METAL) {
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
}
}
if (ImGui::Button("Screenshot")) {
app.screenshot = true;
}
ImGui::Checkbox("Disable buffer padding",
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
@@ -1138,6 +1147,14 @@ int main(int argc, char** argv) {
};
auto postRender = [&app](Engine* engine, View* view, Scene*, Renderer* renderer) {
if (app.screenshot) {
std::ostringstream stringStream;
stringStream << "screenshot" << std::setfill('0') << std::setw(2) << +app.screenshotSeq;
AutomationEngine::exportScreenshot(
view, renderer, stringStream.str() + ".ppm", false, app.automationEngine);
++app.screenshotSeq;
app.screenshot = false;
}
if (app.automationEngine->shouldClose()) {
FilamentApp::get().close();
return;
+18 -90
View File
@@ -20,7 +20,6 @@
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
@@ -47,33 +46,23 @@ using utils::EntityManager;
struct App {
Config config;
VertexBuffer* vb;
VertexBuffer* vb2;
IndexBuffer* ib;
Material* mat;
Camera* cam;
Entity camera;
Skybox* skybox;
Entity renderable;
Entity r2;
};
struct Vertex {
filament::math::float3 position;
filament::math::float2 position;
uint32_t color;
};
float const z = 5;
static const Vertex TRIANGLE_VERTICES[3] = {
{{1, 0, z}, 0xffff0000u},
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3), z}, 0xff00ff00u},
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3), z}, 0xff0000ffu},
};
static Vertex T2[3] = {
TRIANGLE_VERTICES[0],
TRIANGLE_VERTICES[1],
TRIANGLE_VERTICES[2],
{{1, 0}, 0xffff0000u},
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
};
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
@@ -131,36 +120,25 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
}
int main(int argc, char** argv) {
T2[0].position.z = z - 10;
T2[1].position.z = z - 10;
T2[2].position.z = z - 10;
T2[0].color = 0xFF0000FF;
T2[1].color = 0xFF0000FF;
T2[2].color = 0xFF0000FF;
App app{};
app.config.title = "hellotriangle";
app.config.featureLevel = backend::FeatureLevel::FEATURE_LEVEL_0;
handleCommandLineArguments(argc, argv, &app);
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
scene->setSkybox(app.skybox);
view->setPostProcessingEnabled(false);
view->setStencilBufferEnabled(true);
static_assert(sizeof(Vertex) == 16, "Strange vertex size.");
auto builder = VertexBuffer::Builder()
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
app.vb = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, 16)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 12, 16)
.normalized(VertexAttribute::COLOR);
app.vb = builder.build(*engine);
app.vb2 = builder.build(*engine);
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.build(*engine);
app.vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 48, nullptr));
app.vb2->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(T2, 48, nullptr));
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
app.ib = IndexBuffer::Builder()
.indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT)
@@ -171,67 +149,22 @@ int main(int argc, char** argv) {
.package(RESOURCES_BAKEDCOLOR_DATA, RESOURCES_BAKEDCOLOR_SIZE)
.build(*engine);
app.renderable = EntityManager::get().create();
app.r2 = EntityManager::get().create();
auto inst1 = app.mat->createInstance();
inst1->setDepthWrite(false);
inst1->setDepthFunc(MaterialInstance::DepthFunc::A);
inst1->setDepthCulling(false);
inst1->setStencilWrite(true);
inst1->setStencilCompareFunction(MaterialInstance::StencilCompareFunc::E);
inst1->setStencilOpDepthStencilPass(MaterialInstance::StencilOperation::REPLACE);
inst1->setStencilReferenceValue(6);
auto inst2 = app.mat->createInstance();
inst2->setDepthWrite(false);
inst2->setDepthFunc(MaterialInstance::DepthFunc::A);
inst2->setDepthCulling(false);
inst2->setStencilWrite(true);
inst2->setStencilCompareFunction(MaterialInstance::StencilCompareFunc::L);
inst2->setStencilOpDepthStencilPass(MaterialInstance::StencilOperation::REPLACE);
inst2->setStencilReferenceValue(6);
auto& renderableMan = engine->getRenderableManager();
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, inst1)
.material(0, app.mat->getDefaultInstance())
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.renderable);
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, inst2)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb2, app.ib, 0, 3)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.r2);
scene->addEntity(app.renderable);
auto r1inst = renderableMan.getInstance(app.renderable);
renderableMan.setPriority(r1inst, 7);
scene->addEntity(app.r2);
auto r2inst = renderableMan.getInstance(app.r2);
renderableMan.setPriority(r2inst, 6);
app.camera = utils::EntityManager::get().create();
app.cam = engine->createCamera(app.camera);
view->setCamera(app.cam);
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
// engine->destroy(app.skybox);
engine->destroy(app.skybox);
engine->destroy(app.renderable);
engine->destroy(app.mat);
engine->destroy(app.vb);
@@ -241,24 +174,19 @@ int main(int argc, char** argv) {
};
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
constexpr float ZOOM = 1.5;
constexpr float ZOOM = 1.5f;
const uint32_t w = view->getViewport().width;
const uint32_t h = view->getViewport().height;
const float aspect = (float) w / h;
app.cam->setProjection(Camera::Projection::ORTHO,
-aspect * ZOOM, aspect * ZOOM,
-ZOOM, ZOOM, -100, 100);
-ZOOM, ZOOM, 0, 1);
auto& tcm = engine->getTransformManager();
tcm.setTransform(tcm.getInstance(app.renderable),
filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
});
auto preRender = [](Engine*, View* view, Scene*, Renderer* renderer) {
renderer->setClearOptions({ .clearStencil = 0u, .clear = true });
};
FilamentApp::get().run(app.config, setup, cleanup, {}, preRender);
FilamentApp::get().run(app.config, setup, cleanup);
return 0;
}
+40 -3
View File
@@ -615,14 +615,51 @@ static bool processVariables(MaterialBuilder& builder, const JsonishValue& value
}
for (size_t i = 0; i < elements.size(); i++) {
auto elementValue = elements[i];
ParameterPrecision precision = ParameterPrecision::DEFAULT;
MaterialBuilder::Variable v = intToVariable(i);
if (elementValue->getType() != JsonishValue::Type::STRING) {
std::string nameString;
auto elementValue = elements[i];
if (elementValue->getType() == JsonishValue::Type::OBJECT) {
JsonishObject const& jsonObject = *elementValue->toJsonObject();
const JsonishValue* nameValue = jsonObject.getValue("name");
if (!nameValue) {
std::cerr << "variables: entry without 'name' key." << std::endl;
return false;
}
if (nameValue->getType() != JsonishValue::STRING) {
std::cerr << "variables: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* precisionValue = jsonObject.getValue("precision");
if (precisionValue) {
if (precisionValue->getType() != JsonishValue::STRING) {
std::cerr << "variables: precision must be a STRING." << std::endl;
return false;
}
auto precisionString = precisionValue->toJsonString();
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
return logEnumIssue("variables", *precisionString, Enums::map<ParameterPrecision>());
}
}
nameString = nameValue->toJsonString()->getString();
if (precisionValue) {
precision = Enums::toEnum<ParameterPrecision>(
precisionValue->toJsonString()->getString());
}
builder.variable(v, nameString.c_str(), precision);
} else if (elementValue->getType() == JsonishValue::Type::STRING) {
nameString = elementValue->toJsonString()->getString();
builder.variable(v, nameString.c_str());
} else {
std::cerr << "variables: array index " << i << " is not a STRING. found:" <<
JsonishValue::typeToString(elementValue->getType()) << std::endl;
return false;
}
builder.variable(v, elementValue->toJsonString()->getString().c_str());
}
return true;