Initial support for compute

* minimal backend support for compute
- added api to dispatch a compute shader
- added api to create and bind a ssbo
- added api to read back a buffer
Only implemented in the gl backend

* Add a backend compute test suite
* basic support for compute shaders in matc
this is still very much work-in-progress.
We're not supporting images nor ssbo for now.

* rename UniformInterfaceBlock to BufferInterfaceBlock
* augment BufferInterfaceBlock to support ssbo features
- add support for std430
- add support for ssbo
- add support for variable-size array
- add support for memory qualifiers

* reformat MaterialBuilder
* material format: move subpasses outside of parameters
subpasses now are their own json property instead of being a
"parameter".

* refactor parameter() methods to match Buffer/SamplerInterfaceBlock
We're just shuffling the arguments.

* add support for buffers in .mat files
* filamat now generates buffer blocks (ssbo)
* take feature level into consideration when optimizing shaders
* don't store the 'uniform binding' chunk for level 2 materials
this includes some refactoring/cleanups of MaterialParser

* matinfo: fixes for compute
- separate subpasses from parameters
- don't attempt to print material properties
This commit is contained in:
Mathias Agopian
2022-09-27 15:52:40 -07:00
committed by GitHub
parent d291ec739b
commit fabba73b1e
88 changed files with 2558 additions and 1132 deletions

View File

@@ -126,8 +126,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
JNIEnv* env, jclass, jlong nativeBuilder, jint uniformType, jint precision, jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision, name);
builder->parameter(name, (MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}
@@ -137,8 +137,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::UniformType) uniformType, (size_t) size,
(MaterialBuilder::ParameterPrecision) precision, name);
builder->parameter(name, (size_t) size, (MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}
@@ -148,9 +148,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderSampler
jint precision, jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::SamplerType) samplerType,
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision,
name);
builder->parameter(name, (MaterialBuilder::SamplerType) samplerType,
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}

View File

@@ -450,3 +450,23 @@ if (APPLE AND NOT IOS)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
endif()
# ==================================================================================================
# Compute tests
if (NOT IOS AND NOT WEBGL)
add_executable(compute_test
test/ComputeTest.cpp
test/Arguments.cpp
test/test_ComputeBasic.cpp
)
target_link_libraries(compute_test PRIVATE
backend
getopt
gtest
)
set_target_properties(compute_test PROPERTIES FOLDER Tests)
endif()

View File

@@ -50,6 +50,7 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.
// Per feature level caps
// Use (int)FeatureLevel to index this array
@@ -313,7 +314,8 @@ enum class ElementType : uint8_t {
//! Buffer object binding type
enum class BufferObjectBinding : uint8_t {
VERTEX,
UNIFORM
UNIFORM,
SHADER_STORAGE
};
//! Face culling Mode
@@ -897,7 +899,8 @@ struct RasterState {
enum class ShaderStage : uint8_t {
VERTEX = 0,
FRAGMENT = 1
FRAGMENT = 1,
COMPUTE = 2
};
static constexpr size_t PIPELINE_STAGE_COUNT = 2;
@@ -905,7 +908,8 @@ enum class ShaderStageFlags : uint8_t {
NONE = 0,
VERTEX = 0x1,
FRAGMENT = 0x2,
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT
COMPUTE = 0x4,
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
};
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
@@ -914,6 +918,8 @@ static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage t
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
case ShaderStage::FRAGMENT:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT));
case ShaderStage::COMPUTE:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE));
}
}

View File

@@ -34,7 +34,7 @@ namespace filament::backend {
class Program {
public:
static constexpr size_t SHADER_TYPE_COUNT = 2;
static constexpr size_t SHADER_TYPE_COUNT = 3;
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;

View File

@@ -406,12 +406,17 @@ DECL_DRIVER_API_N(bindUniformBuffer,
uint32_t, index,
backend::BufferObjectHandle, ubh)
DECL_DRIVER_API_N(bindUniformBufferRange,
DECL_DRIVER_API_N(bindBufferRange,
BufferObjectBinding, bindingType,
uint32_t, index,
backend::BufferObjectHandle, ubh,
uint32_t, offset,
uint32_t, size)
DECL_DRIVER_API_N(unbindBuffer,
BufferObjectBinding, bindingType,
uint32_t, index)
DECL_DRIVER_API_N(bindSamplers,
uint32_t, index,
backend::SamplerGroupHandle, sbh)
@@ -444,6 +449,12 @@ DECL_DRIVER_API_N(readPixels,
uint32_t, height,
backend::PixelBufferDescriptor&&, data)
DECL_DRIVER_API_N(readBufferSubData,
backend::BufferObjectHandle, src,
uint32_t, offset,
uint32_t, size,
backend::BufferDescriptor&&, data)
/*
* Rendering operations
* --------------------
@@ -462,6 +473,11 @@ DECL_DRIVER_API_N(draw,
backend::RenderPrimitiveHandle, rph,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
#pragma clang diagnostic pop
#undef EXPAND

View File

@@ -965,8 +965,14 @@ void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
};
}
void MetalDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
uint32_t offset, uint32_t size) {
void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
assert_invariant(index < UNIFORM_BUFFER_COUNT);
auto* bo = handle_cast<MetalBufferObject>(boh);
auto* currentBo = mContext->uniformState[index].buffer;
@@ -981,6 +987,10 @@ void MetalDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject>
};
}
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
// TODO: implement unbindBuffer()
}
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
auto sb = handle_cast<MetalSamplerGroup>(sbh);
mContext->samplerBindings[index] = sb;
@@ -1108,6 +1118,12 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
}];
}
void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
// TODO: implement readBufferSubData
scheduleDestroy(std::move(p));
}
void MetalDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
@@ -1502,6 +1518,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
instanceCount:instanceCount];
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"beginTimerQuery must be called outside of a render pass.");

View File

@@ -342,7 +342,8 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
using MetalFunctionPtr = __strong id<MTLFunction>*;
static_assert(Program::SHADER_TYPE_COUNT == 2, "Only vertex and fragment shaders expected.");
// FIXME: rework this to handle compute
//static_assert(Program::SHADER_TYPE_COUNT == 2, "Only vertex and fragment shaders expected.");
MetalFunctionPtr shaderFunctions[2] = { &vertexFunction, &fragmentFunction };
const auto& sources = program.getShadersSource();
@@ -424,6 +425,9 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
switch (type) {
case ShaderStage::VERTEX: return vertexSamplerBlockInfo;
case ShaderStage::FRAGMENT: return fragmentSamplerBlockInfo;
case ShaderStage::COMPUTE:
// FIXME: implement ShaderStage::COMPUTE
std::terminate();
}
};

View File

@@ -273,8 +273,11 @@ void NoopDriver::commit(Handle<HwSwapChain> sch) {
void NoopDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh) {
}
void NoopDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
uint32_t offset, uint32_t size) {
void NoopDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
}
void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
}
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
@@ -301,6 +304,11 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
scheduleDestroy(std::move(p));
}
void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
scheduleDestroy(std::move(p));
}
void NoopDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
@@ -311,6 +319,9 @@ void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph
uint32_t instanceCount) {
}
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
}
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
}

View File

@@ -119,6 +119,8 @@ constexpr inline GLenum getBufferBindingType(BufferObjectBinding bindingType) no
return GL_ARRAY_BUFFER;
case BufferObjectBinding::UNIFORM:
return GL_UNIFORM_BUFFER;
case BufferObjectBinding::SHADER_STORAGE:
return GL_SHADER_STORAGE_BUFFER;
}
}

View File

@@ -309,7 +309,7 @@ public:
GLsizeiptr size = 0;
} buffers[MAX_BUFFER_BINDINGS];
} targets[2]; // there are only 2 indexed buffer target (uniform and transform feedback)
GLuint genericBinding[8] = { 0 };
GLuint genericBinding[9] = { 0 };
} buffers;
struct {
@@ -458,17 +458,18 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
constexpr size_t OpenGLContext::getIndexForBufferTarget(GLenum target) noexcept {
size_t index = 0;
switch (target) {
// The indexed buffers MUST be first in this list
// The indexed buffers MUST be first in this list (those usable with bindBufferRange)
case GL_UNIFORM_BUFFER: index = 0; break;
case GL_TRANSFORM_FEEDBACK_BUFFER: index = 1; break;
case GL_SHADER_STORAGE_BUFFER: index = 2; break;
case GL_ARRAY_BUFFER: index = 2; break;
case GL_COPY_READ_BUFFER: index = 3; break;
case GL_COPY_WRITE_BUFFER: index = 4; break;
case GL_ELEMENT_ARRAY_BUFFER: index = 5; break;
case GL_PIXEL_PACK_BUFFER: index = 6; break;
case GL_PIXEL_UNPACK_BUFFER: index = 7; break;
default: index = 8; break; // should never happen
case GL_ARRAY_BUFFER: index = 3; break;
case GL_COPY_READ_BUFFER: index = 4; break;
case GL_COPY_WRITE_BUFFER: index = 5; break;
case GL_ELEMENT_ARRAY_BUFFER: index = 6; break;
case GL_PIXEL_PACK_BUFFER: index = 7; break;
case GL_PIXEL_UNPACK_BUFFER: index = 8; break;
default: index = 9; break; // should never happen
}
assert_invariant(index < sizeof(state.buffers.genericBinding)/sizeof(state.buffers.genericBinding[0])); // NOLINT(misc-redundant-expression)
return index;
@@ -529,7 +530,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size) noexcept {
size_t targetIndex = getIndexForBufferTarget(target);
assert_invariant(targetIndex <= 1); // validity check
assert_invariant(targetIndex <= 2); // validity check
// this ALSO sets the generic binding
if ( state.buffers.targets[targetIndex].buffers[index].name != buffer

View File

@@ -2574,15 +2574,31 @@ void OpenGLDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh)
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
uint32_t offset, uint32_t size) {
void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
DEBUG_MARKER()
auto& gl = mContext;
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
GLBufferObject* ub = handle_cast<GLBufferObject *>(ubh);
assert_invariant(ub->gl.binding == GL_UNIFORM_BUFFER);
GLenum target = GLUtils::getBufferBindingType(bindingType);
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
ub->gl.binding == target);
assert_invariant(offset + size <= ub->byteCount);
gl.bindBufferRange(ub->gl.binding, GLuint(index), ub->gl.id, offset, size);
gl.bindBufferRange(target, GLuint(index), ub->gl.id, offset, size);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
DEBUG_MARKER()
auto& gl = mContext;
GLenum target = GLUtils::getBufferBindingType(bindingType);
gl.bindBufferRange(target, GLuint(index), 0, 0, 0);
CHECK_GL_ERROR(utils::slog.e)
}
@@ -2759,6 +2775,58 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
});
}
void OpenGLDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
auto& gl = mContext;
GLBufferObject const* bo = handle_cast<GLBufferObject const*>(boh);
// TODO: measure the two solutions
if constexpr (true) {
// schedule a copy of the buffer we're reading into a PBO, this *should* happen
// asynchronously without stalling the CPU.
GLuint pbo;
glGenBuffers(1, &pbo);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, (GLsizeiptr)size, nullptr, GL_STATIC_DRAW);
gl.bindBuffer(bo->gl.binding, bo->gl.id);
glCopyBufferSubData(bo->gl.binding, GL_PIXEL_PACK_BUFFER, offset, 0, size);
gl.bindBuffer(bo->gl.binding, 0);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
CHECK_GL_ERROR(utils::slog.e)
// then, we schedule a mapBuffer of the PBO later, once the fence has signaled
auto* pUserBuffer = new BufferDescriptor(std::move(p));
whenGpuCommandsComplete([this, size, pbo, pUserBuffer]() mutable {
BufferDescriptor& p = *pUserBuffer;
auto& gl = mContext;
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* vaddr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT);
if (vaddr) {
memcpy(p.buffer, vaddr, size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
scheduleDestroy(std::move(p));
delete pUserBuffer;
CHECK_GL_ERROR(utils::slog.e)
});
} else {
gl.bindBuffer(bo->gl.binding, bo->gl.id);
// TODO: this glMapBufferRange may stall. Ideally we want to use whenGpuCommandsComplete
// but that's tricky because boh could be destroyed right after this call.
void* vaddr = glMapBufferRange(bo->gl.binding, offset, size, GL_MAP_READ_BIT);
if (vaddr) {
memcpy(p.buffer, vaddr, size);
glUnmapBuffer(bo->gl.binding);
}
gl.bindBuffer(bo->gl.binding, 0);
scheduleDestroy(std::move(p));
CHECK_GL_ERROR(utils::slog.e)
}
}
void OpenGLDriver::whenGpuCommandsComplete(std::function<void()> fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mGpuCommandCompleteOps.emplace_back(sync, std::move(fn));
@@ -3102,6 +3170,31 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
#endif
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
executeRenderPassOps();
OpenGLProgram* p = handle_cast<OpenGLProgram*>(program);
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
useProgram(p);
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
#endif
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<OpenGLDriver>;

View File

@@ -120,15 +120,12 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
// build all shaders
UTILS_NOUNROLL
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
ShaderStage type = static_cast<ShaderStage>(i);
const ShaderStage stage = static_cast<ShaderStage>(i);
GLenum glShaderType;
switch (type) {
case ShaderStage::VERTEX:
glShaderType = GL_VERTEX_SHADER;
break;
case ShaderStage::FRAGMENT:
glShaderType = GL_FRAGMENT_SHADER;
break;
switch (stage) {
case ShaderStage::VERTEX: glShaderType = GL_VERTEX_SHADER; break;
case ShaderStage::FRAGMENT: glShaderType = GL_FRAGMENT_SHADER; break;
case ShaderStage::COMPUTE: glShaderType = GL_COMPUTE_SHADER; break;
}
if (UTILS_LIKELY(!shadersSource[i].empty())) {
@@ -428,6 +425,7 @@ void logCompilationError(io::ostream& out, ShaderStage shaderType,
switch (type) {
case ShaderStage::VERTEX: return "vertex";
case ShaderStage::FRAGMENT: return "fragment";
case ShaderStage::COMPUTE: return "compute";
}
};

View File

@@ -19,7 +19,11 @@
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
#include <GLES3/gl31.h>
#if defined(__EMSCRIPTEN__)
# include <GLES3/gl3.h>
#else
# include <GLES3/gl31.h>
#endif
#include <GLES2/gl2ext.h>
/* The Android NDK doesn't expose extensions, fake it with eglGetProcAddress */
@@ -157,6 +161,8 @@
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
* requires ES3.1 headers */
#if !defined(GL_ES_VERSION_3_1)
#define GL_SHADER_STORAGE_BUFFER 0x90D2
#define GL_COMPUTE_SHADER 0x91B9
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
#define GL_TIME_ELAPSED 0x88BF

View File

@@ -367,6 +367,7 @@ io::ostream& operator<<(io::ostream& out, BufferObjectBinding binding) {
switch (binding) {
CASE(BufferObjectBinding, VERTEX)
CASE(BufferObjectBinding, UNIFORM)
CASE(BufferObjectBinding, SHADER_STORAGE)
}
return out;
}
@@ -498,8 +499,11 @@ io::ostream& operator<<(io::ostream& stream, ShaderStageFlags stageFlags) {
case ShaderStageFlags::FRAGMENT:
str = "{ fragment }";
break;
case ShaderStageFlags::COMPUTE:
str = "{ compute }";
break;
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
str = "{ vertex | fragment }";
str = "{ vertex | fragment | compute }";
break;
}
return stream << str;

View File

@@ -111,6 +111,10 @@ void VulkanBuffer::loadFromCpu(VulkanContext& context, VulkanStagePool& stagePoo
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
0, 0, nullptr, 1, &barrier, 0, nullptr);
}
if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
// TODO: implement me
}
}
} // namespace filament::backend

View File

@@ -1470,12 +1470,22 @@ void VulkanDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
mPipelineCache.bindUniformBuffer((uint32_t) index, bo->buffer.getGpuBuffer(), offset, size);
}
void VulkanDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
uint32_t offset, uint32_t size) {
void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
auto* bo = handle_cast<VulkanBufferObject*>(boh);
mPipelineCache.bindUniformBuffer((uint32_t)index, bo->buffer.getGpuBuffer(), offset, size);
}
void VulkanDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
// TODO: implement unbindBuffer()
}
void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
auto* hwsb = handle_cast<VulkanSamplerGroup*>(sbh);
mSamplerBindings[index] = hwsb;
@@ -1689,6 +1699,12 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
scheduleDestroy(std::move(pbd));
}
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
// TODO: implement readBufferSubData
scheduleDestroy(std::move(p));
}
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
assert_invariant(mContext.currentRenderPass.renderPass == VK_NULL_HANDLE);
@@ -1937,6 +1953,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanCommandBuffer const* commands = &mContext.commands->get();
VulkanTimerQuery* vtq = handle_cast<VulkanTimerQuery*>(tqh);

View File

@@ -143,7 +143,9 @@ inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BufferObjectBinding::UNIFORM:
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
// when adding more buffer types here, make sure to update VulkanBuffer::loadFromCpu()
case BufferObjectBinding::SHADER_STORAGE:
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
// if necessary.
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 "ComputeTest.h"
#include "PlatformRunner.h"
#include <backend/Platform.h>
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/DriverApi.h"
using namespace filament;
using namespace filament::backend;
int main(int argc, char* argv[]) {
auto backend = (Backend)test::parseArgumentsForBackend(argc, argv);
ComputeTest::init(backend);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
// ------------------------------------------------------------------------------------------------
static constexpr size_t CONFIG_MIN_COMMAND_BUFFERS_SIZE = 1 * 1024 * 1024;
static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND_BUFFERS_SIZE;
Backend ComputeTest::sBackend = Backend::NOOP;
void ComputeTest::init(Backend backend) {
sBackend = backend;
}
ComputeTest::ComputeTest()
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
}
ComputeTest::~ComputeTest() = default;
void ComputeTest::SetUp() {
Test::SetUp();
auto backend = sBackend;
DefaultPlatform* platform = DefaultPlatform::create(&backend);
GTEST_ASSERT_EQ(uint8_t(backend), uint8_t(sBackend));
driver = platform->createDriver(nullptr, {});
commandStream = std::make_unique<CommandStream>(*driver, commandBufferQueue.getCircularBuffer());
// We need at least feature level 2 to run the Compute tests
if (driver->getFeatureLevel() < FeatureLevel::FEATURE_LEVEL_2) {
GTEST_SKIP();
}
}
void ComputeTest::TearDown() {
Test::TearDown();
flushAndWait();
driver->terminate();
delete driver;
}
void ComputeTest::executeCommands() {
commandBufferQueue.flush();
auto buffers = commandBufferQueue.waitForCommands();
for (auto& item : buffers) {
if (UTILS_LIKELY(item.begin)) {
getDriverApi().execute(item.begin);
commandBufferQueue.releaseBuffer(item);
}
}
}
void ComputeTest::flushAndWait(uint64_t timeout) {
auto& api = getDriverApi();
auto fence = api.createFence();
api.finish();
executeCommands();
api.wait(fence, timeout);
api.destroyFence(fence);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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.
*/
#ifndef TNT_COMPUTE_TEST_H
#define TNT_COMPUTE_TEST_H
#include <gtest/gtest.h>
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/DriverApi.h"
class ComputeTest : public ::testing::Test {
static filament::backend::Backend sBackend;
filament::backend::Driver* driver = nullptr;
filament::backend::CommandBufferQueue commandBufferQueue;
std::unique_ptr<filament::backend::DriverApi> commandStream;
protected:
void SetUp() override;
void TearDown() override;
void executeCommands();
void flushAndWait(uint64_t timeout = 1000);
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
static filament::backend::Backend getBackend() noexcept { return sBackend; };
bool isMobile() noexcept { return driver->getShaderModel() == filament::backend::ShaderModel::MOBILE; };
public:
static void init(filament::backend::Backend backend);
ComputeTest();
~ComputeTest() override;
};
#endif // TNT_COMPUTE_TEST_H

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 "ComputeTest.h"
using namespace filament;
using namespace filament::backend;
TEST_F(ComputeTest, basic) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
)"};
std::string_view shader_spirv = {R"(
// TODO: spirv test
)"};
std::string_view shader;
switch (getBackend()) {
case Backend::OPENGL: shader = isMobile() ? shader_gles310 : shader_gl450; break;
case Backend::VULKAN: shader = shader_spirv; break;
case Backend::METAL: shader = shader_msl; break;
default:
GTEST_FATAL_FAILURE_("unexpected backend");
}
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
Handle<HwProgram> ph = driver.createProgram(std::move(program));
driver.dispatchCompute(ph, { 1, 1, 1 });
driver.destroyProgram(ph);
driver.finish();
executeCommands();
getDriver().purge();
}
TEST_F(ComputeTest, copy) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
layout(std430) buffer;
layout(binding = 0) writeonly buffer Output { float elements[]; } output_data;
layout(binding = 1) readonly buffer Input0 { float elements[]; } input_data;
void main() {
uint ident = gl_GlobalInvocationID.x;
output_data.elements[ident] = input_data.elements[ident];
}
)"};
std::string_view shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
layout(std430) buffer;
layout(binding = 0) writeonly buffer Output { float elements[]; } output_data;
layout(binding = 1) readonly buffer Input0 { float elements[]; } input_data;
void main() {
uint ident = gl_GlobalInvocationID.x;
output_data.elements[ident] = input_data.elements[ident];
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
)"};
std::string_view shader_spirv = {R"(
// TODO: spirv test
)"};
std::string_view shader;
switch (getBackend()) {
case Backend::OPENGL: shader = isMobile() ? shader_gles310 : shader_gl450; break;
case Backend::VULKAN: shader = shader_spirv; break;
case Backend::METAL: shader = shader_msl; break;
default:
GTEST_FATAL_FAILURE_("unexpected backend");
}
size_t groupSize = 16;
size_t groupCount = 1024;
size_t size = groupSize * groupCount * sizeof(float);
std::vector<float> data(groupSize * groupCount);
std::generate(data.begin(), data.end(), [v = 0.0f]() mutable {
v = v + 1.0f;
return v;
});
auto output_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
auto input_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
driver.updateBufferObject(input_data, { data.data(), size }, 0);
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
Handle<HwProgram> ph = driver.createProgram(std::move(program));
driver.bindBufferRange(BufferObjectBinding::SHADER_STORAGE, 0, output_data, 0, size);
driver.bindBufferRange(BufferObjectBinding::SHADER_STORAGE, 1, input_data, 0, size);
driver.dispatchCompute(ph, { groupCount, 1, 1 });
driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 0);
driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 1);
float* const user = (float*)malloc(size);
driver.readBufferSubData(output_data, 0, size, { user, size });
driver.destroyProgram(ph);
driver.destroyBufferObject(input_data);
driver.destroyBufferObject(output_data);
driver.finish();
executeCommands();
getDriver().purge();
// TODO: check buffer content
EXPECT_EQ(memcmp(user, data.data(), size), 0);
free(user);
}

View File

@@ -34,7 +34,7 @@ class Material;
class Texture;
class TextureSampler;
class UniformBuffer;
class UniformInterfaceBlock;
class BufferInterfaceBlock;
class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
template<size_t N>

View File

@@ -30,7 +30,7 @@ using namespace backend;
template<size_t Size>
UTILS_NOINLINE
void FMaterialInstance::setParameterUntypedImpl(std::string_view name, const void* value) {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getUniformOffset(name, 0);
ssize_t offset = mMaterial->getUniformInterfaceBlock().getFieldOffset(name, 0);
if (UTILS_LIKELY(offset >= 0)) {
mUniforms.setUniformUntyped<Size>(size_t(offset), value); // handles specialization for mat3f
}
@@ -49,7 +49,7 @@ inline void FMaterialInstance::setParameterImpl(std::string_view name, T const&
// specialization for mat3f
template<>
inline void FMaterialInstance::setParameterImpl(std::string_view name, mat3f const& value) {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getUniformOffset(name, 0);
ssize_t offset = mMaterial->getUniformInterfaceBlock().getFieldOffset(name, 0);
if (UTILS_LIKELY(offset >= 0)) {
mUniforms.setUniform(size_t(offset), value);
}
@@ -61,7 +61,7 @@ template<size_t Size>
UTILS_NOINLINE
void FMaterialInstance::setParameterUntypedImpl(std::string_view name,
const void* value, size_t count) {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getUniformOffset(name, 0);
ssize_t offset = mMaterial->getUniformInterfaceBlock().getFieldOffset(name, 0);
if (UTILS_LIKELY(offset >= 0)) {
mUniforms.setUniformArrayUntyped<Size>(size_t(offset), value, count);
}

View File

@@ -26,7 +26,7 @@
#include <private/filament/SamplerBindingsInfo.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <private/filament/Variant.h>
@@ -124,33 +124,29 @@ bool MaterialParser::getFeatureLevel(uint8_t* value) const noexcept {
}
bool MaterialParser::getName(utils::CString* cstring) const noexcept {
ChunkType type = ChunkType::MaterialName;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialName);
if (start == end) return false;
Unflattener unflattener(start, end);
return unflattener.read(cstring);
}
bool MaterialParser::getUIB(UniformInterfaceBlock* uib) const noexcept {
auto type = MaterialUib;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
bool MaterialParser::getUIB(BufferInterfaceBlock* uib) const noexcept {
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialUib);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkUniformInterfaceBlock::unflatten(unflattener, uib);
}
bool MaterialParser::getSIB(SamplerInterfaceBlock* sib) const noexcept {
auto type = MaterialSib;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSib);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkSamplerInterfaceBlock::unflatten(unflattener, sib);
}
bool MaterialParser::getSubpasses(SubpassInfo* subpass) const noexcept {
auto type = MaterialSubpass;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSubpass);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkSubpassInterfaceBlock::unflatten(unflattener, subpass);
}
@@ -165,9 +161,8 @@ bool MaterialParser::getMaterialProperties(uint64_t* value) const noexcept {
bool MaterialParser::getUniformBlockBindings(
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>* value) const noexcept {
auto type = MaterialUniformBindings;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialUniformBindings);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkUniformBlockBindings::unflatten(unflattener, value);
}
@@ -175,9 +170,8 @@ bool MaterialParser::getUniformBlockBindings(
bool MaterialParser::getSamplerBlockBindings(
SamplerGroupBindingInfoList* pSamplerGroupInfoList,
SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept {
auto type = MaterialSamplerBindings;
const uint8_t* start = mImpl.mChunkContainer.getChunkStart(type);
const uint8_t* end = mImpl.mChunkContainer.getChunkEnd(type);
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSamplerBindings);
if (start == end) return false;
Unflattener unflattener(start, end);
return ChunkSamplerBlockBindings::unflatten(unflattener,
pSamplerGroupInfoList, pSamplerBindingToNameMap);
@@ -316,9 +310,9 @@ bool MaterialParser::getShader(ShaderContent& shader,
bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener,
filament::UniformInterfaceBlock* uib) {
filament::BufferInterfaceBlock* uib) {
UniformInterfaceBlock::Builder builder = UniformInterfaceBlock::Builder();
BufferInterfaceBlock::Builder builder = BufferInterfaceBlock::Builder();
CString name;
if (!unflattener.read(&name)) {
@@ -358,8 +352,8 @@ bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener,
// a size of 1 means not an array
builder.add({{{ fieldName.data(), fieldName.size() },
uint32_t(fieldSize == 1 ? 0 : fieldSize),
UniformInterfaceBlock::Type(fieldType),
UniformInterfaceBlock::Precision(fieldPrecision) }});
BufferInterfaceBlock::Type(fieldType),
BufferInterfaceBlock::Precision(fieldPrecision) }});
}
*uib = builder.build();

View File

@@ -40,7 +40,7 @@ class Unflattener;
namespace filament {
class UniformInterfaceBlock;
class BufferInterfaceBlock;
class SamplerInterfaceBlock;
struct SubpassInfo;
@@ -63,7 +63,7 @@ public:
bool getMaterialVersion(uint32_t* value) const noexcept;
bool getFeatureLevel(uint8_t* value) const noexcept;
bool getName(utils::CString*) const noexcept;
bool getUIB(UniformInterfaceBlock* uib) const noexcept;
bool getUIB(BufferInterfaceBlock* uib) const noexcept;
bool getSIB(SamplerInterfaceBlock* sib) const noexcept;
bool getSubpasses(SubpassInfo* subpass) const noexcept;
bool getShaderModels(uint32_t* value) const noexcept;
@@ -144,7 +144,7 @@ private:
};
struct ChunkUniformInterfaceBlock {
static bool unflatten(filaflat::Unflattener& unflattener, UniformInterfaceBlock* uib);
static bool unflatten(filaflat::Unflattener& unflattener, BufferInterfaceBlock* uib);
};
struct ChunkSamplerInterfaceBlock {

View File

@@ -795,14 +795,16 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
// bind per-renderable uniform block. there is no need to attempt to skip this command
// because the backends already do this.
driver.bindUniformBufferRange(+UniformBindingPoints::PER_RENDERABLE,
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE,
(info.instanceCount > 1) ? mInstancedUboHandle : uboHandle,
info.index * sizeof(PerRenderableData),
sizeof(PerRenderableUib));
if (UTILS_UNLIKELY(info.skinningHandle)) {
// note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations
driver.bindUniformBufferRange(+UniformBindingPoints::PER_RENDERABLE_BONES,
driver.bindBufferRange(BufferObjectBinding::UNIFORM,
+UniformBindingPoints::PER_RENDERABLE_BONES,
info.skinningHandle,
info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData),
sizeof(PerRenderableBoneUib));

View File

@@ -45,7 +45,7 @@
#include "private/backend/DriverApi.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>

View File

@@ -26,7 +26,7 @@
#include <private/filament/EngineEnums.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h>
@@ -110,8 +110,12 @@ Material* Material::Builder::build(Engine& engine) {
materialParser->getName(&name);
slog.e << "The material '" << name.c_str_safe() << "' was not built for ";
switch (shaderModel) {
case ShaderModel::MOBILE: slog.e << "mobile.\n"; break;
case ShaderModel::DESKTOP: slog.e << "desktop.\n"; break;
case ShaderModel::MOBILE:
slog.e << "mobile.\n";
break;
case ShaderModel::DESKTOP:
slog.e << "desktop.\n";
break;
}
slog.e << "Compiled material contains shader models 0x"
<< io::hex << shaderModels.getValue() << io::dec << "." << io::endl;
@@ -156,9 +160,10 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
// read the uniform binding list
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> uniformBlockBindings;
success = parser->getUniformBlockBindings(&mUniformBlockBindings);
assert_invariant(success);
assert_invariant(success || mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2);
success = parser->getSamplerBlockBindings(&mSamplerGroupBindingInfoList, &mSamplerBindingToNameMap);
success = parser->getSamplerBlockBindings(
&mSamplerGroupBindingInfoList, &mSamplerBindingToNameMap);
assert_invariant(success);
#if FILAMENT_ENABLE_MATDBG
@@ -329,8 +334,8 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
}
bool FMaterial::hasParameter(const char* name) const noexcept {
return mUniformInterfaceBlock.hasUniform(name) ||
mSamplerInterfaceBlock.hasSampler(name) ||
return mUniformInterfaceBlock.hasField(name) ||
mSamplerInterfaceBlock.hasSampler(name) ||
mSubpassInfo.name == utils::CString(name);
}
@@ -338,9 +343,9 @@ bool FMaterial::isSampler(const char* name) const noexcept {
return mSamplerInterfaceBlock.hasSampler(name);
}
UniformInterfaceBlock::UniformInfo const* FMaterial::reflect(
BufferInterfaceBlock::FieldInfo const* FMaterial::reflect(
std::string_view name) const noexcept {
return mUniformInterfaceBlock.getUniformInfo(name);
return mUniformInterfaceBlock.getFieldInfo(name);
}
void FMaterial::prepareProgramSlow(Variant variant) const noexcept {
@@ -349,10 +354,12 @@ void FMaterial::prepareProgramSlow(Variant variant) const noexcept {
case MaterialDomain::SURFACE:
getSurfaceProgramSlow(variant);
break;
case MaterialDomain::POST_PROCESS:
getPostProcessProgramSlow(variant);
break;
case MaterialDomain::COMPUTE:
// TODO: implement MaterialDomain::COMPUTE
break;
}
}
@@ -452,7 +459,7 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexce
size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
count = std::min(count, getParameterCount());
const auto& uniforms = mUniformInterfaceBlock.getUniformInfoList();
const auto& uniforms = mUniformInterfaceBlock.getFieldInfoList();
size_t i = 0;
size_t uniformCount = std::min(count, size_t(uniforms.size()));
for ( ; i < uniformCount; i++) {

View File

@@ -53,7 +53,7 @@ public:
void terminate(FEngine& engine);
// return the uniform interface block for this material
const UniformInterfaceBlock& getUniformInterfaceBlock() const noexcept {
const BufferInterfaceBlock& getUniformInterfaceBlock() const noexcept {
return mUniformInterfaceBlock;
}
@@ -69,7 +69,7 @@ public:
bool isSampler(const char* name) const noexcept;
UniformInterfaceBlock::UniformInfo const* reflect(std::string_view name) const noexcept;
BufferInterfaceBlock::FieldInfo const* reflect(std::string_view name) const noexcept;
FMaterialInstance const* getDefaultInstance() const noexcept { return &mDefaultInstance; }
FMaterialInstance* getDefaultInstance() noexcept { return &mDefaultInstance; }
@@ -140,9 +140,9 @@ public:
}
size_t getParameterCount() const noexcept {
return mUniformInterfaceBlock.getUniformInfoList().size() +
mSamplerInterfaceBlock.getSamplerInfoList().size() +
(mSubpassInfo.isValid ? 1 : 0);
return mUniformInterfaceBlock.getFieldInfoList().size() +
mSamplerInterfaceBlock.getSamplerInfoList().size() +
(mSubpassInfo.isValid ? 1 : 0);
}
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
@@ -224,7 +224,7 @@ private:
FMaterialInstance mDefaultInstance;
SamplerInterfaceBlock mSamplerInterfaceBlock;
UniformInterfaceBlock mUniformInterfaceBlock;
BufferInterfaceBlock mUniformInterfaceBlock;
SubpassInfo mSubpassInfo;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> mUniformBlockBindings;
SamplerGroupBindingInfoList mSamplerGroupBindingInfoList;

View File

@@ -30,7 +30,9 @@ material {
{
type : float4,
name : vignetteColor
},
}
],
subpasses : [
{
type : subpassInput,
format : float,

View File

@@ -7,7 +7,9 @@ material {
type : float,
name : direction,
precision: low
},
}
],
subpasses : [
{
type : subpassInput,
format : float,

View File

@@ -32,7 +32,7 @@
#include <filament/Material.h>
#include <filament/Engine.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/UibStructs.h>
#include <private/backend/BackendUtils.h>
@@ -279,30 +279,30 @@ TEST(FilamentTest, TransformManager) {
TEST(FilamentTest, UniformInterfaceBlock) {
UniformInterfaceBlock::Builder b;
BufferInterfaceBlock::Builder b;
b.name("TestUniformInterfaceBlock");
b.add({
{ "a_float_0", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_1", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_2", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_3", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_vec4_0", 0, UniformInterfaceBlock::Type::FLOAT4 },
{ "a_float_4", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_5", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_6", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_vec3_0", 0, UniformInterfaceBlock::Type::FLOAT3 },
{ "a_float_7", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float[3]", 3, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_8", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_mat3_0", 0, UniformInterfaceBlock::Type::MAT3 },
{ "a_mat4_0", 0, UniformInterfaceBlock::Type::MAT4 },
{ "a_mat3[3]", 3, UniformInterfaceBlock::Type::MAT3 }
{ "a_float_0", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_1", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_2", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_3", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_vec4_0", 0, BufferInterfaceBlock::Type::FLOAT4 },
{ "a_float_4", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_5", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_6", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_vec3_0", 0, BufferInterfaceBlock::Type::FLOAT3 },
{ "a_float_7", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float[3]", 3, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_8", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_mat3_0", 0, BufferInterfaceBlock::Type::MAT3 },
{ "a_mat4_0", 0, BufferInterfaceBlock::Type::MAT4 },
{ "a_mat3[3]", 3, BufferInterfaceBlock::Type::MAT3 }
});
UniformInterfaceBlock ib(b.build());
auto const& info = ib.getUniformInfoList();
BufferInterfaceBlock ib(b.build());
auto const& info = ib.getFieldInfoList();
// test that 4 floats are packed together
EXPECT_EQ(0, info[0].offset);
@@ -402,27 +402,27 @@ TEST(FilamentTest, UniformBuffer) {
EXPECT_EQ(offsetof(ubo, m1)/4, info[13].offset);
};
UniformInterfaceBlock::Builder b;
BufferInterfaceBlock::Builder b;
b.name("TestUniformBuffer");
b.add({
{ "a_float_0", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_1", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_2", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_3", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_vec4_0", 0, UniformInterfaceBlock::Type::FLOAT4 },
{ "a_float_4", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_5", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_6", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_vec3_0", 0, UniformInterfaceBlock::Type::FLOAT3 },
{ "a_float_7", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_float[3]",3, UniformInterfaceBlock::Type::FLOAT },
{ "a_float_8", 0, UniformInterfaceBlock::Type::FLOAT },
{ "a_mat3_0", 0, UniformInterfaceBlock::Type::MAT3 },
{ "a_mat4_0", 0, UniformInterfaceBlock::Type::MAT4 },
{ "a_float_0", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_1", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_2", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_3", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_vec4_0", 0, BufferInterfaceBlock::Type::FLOAT4 },
{ "a_float_4", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_5", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_6", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_vec3_0", 0, BufferInterfaceBlock::Type::FLOAT3 },
{ "a_float_7", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_float[3]",3, BufferInterfaceBlock::Type::FLOAT },
{ "a_float_8", 0, BufferInterfaceBlock::Type::FLOAT },
{ "a_mat3_0", 0, BufferInterfaceBlock::Type::MAT3 },
{ "a_mat4_0", 0, BufferInterfaceBlock::Type::MAT4 },
});
UniformInterfaceBlock ib(b.build());
BufferInterfaceBlock ib(b.build());
CHECK2(ib.getUniformInfoList());
CHECK2(ib.getFieldInfoList());
EXPECT_EQ(sizeof(ubo), ib.getSize());
@@ -464,46 +464,46 @@ TEST(FilamentTest, UniformBuffer) {
}
TEST(FilamentTest, UniformBufferSize1) {
UniformInterfaceBlock::Builder b;
BufferInterfaceBlock::Builder b;
b.name("UniformBufferSize1");
b.add({
{ "f4a", 0, UniformInterfaceBlock::Type::FLOAT4 }, // offset = 0
{ "f4b", 0, UniformInterfaceBlock::Type::FLOAT4 }, // offset = 16
{ "f1a", 0, UniformInterfaceBlock::Type::FLOAT }, // offset = 32
{ "f1b", 0, UniformInterfaceBlock::Type::FLOAT }, // offset = 36
{ "f4a", 0, BufferInterfaceBlock::Type::FLOAT4 }, // offset = 0
{ "f4b", 0, BufferInterfaceBlock::Type::FLOAT4 }, // offset = 16
{ "f1a", 0, BufferInterfaceBlock::Type::FLOAT }, // offset = 32
{ "f1b", 0, BufferInterfaceBlock::Type::FLOAT }, // offset = 36
});
UniformInterfaceBlock uib(b.build());
BufferInterfaceBlock uib(b.build());
UniformBuffer buffer(uib.getSize());
float4 f4(1.0f);
ssize_t f4_offset = uib.getUniformOffset("f4a", 0);
ssize_t f4_offset = uib.getFieldOffset("f4a", 0);
buffer.setUniformArray(f4_offset, &f4, 1);
float f1(1.0f);
ssize_t f1_offset = uib.getUniformOffset("f1b", 0);
ssize_t f1_offset = uib.getFieldOffset("f1b", 0);
buffer.setUniformArray(f1_offset, &f1, 1);
buffer.invalidate();
}
TEST(FilamentTest, UniformBufferSize2) {
UniformInterfaceBlock::Builder b;
BufferInterfaceBlock::Builder b;
b.name("UniformBufferSize2");
b.add({
{ "f4a", 0, UniformInterfaceBlock::Type::FLOAT4 }, // offset = 0
{ "f4b", 0, UniformInterfaceBlock::Type::FLOAT4 }, // offset = 16
{ "f1a", 0, UniformInterfaceBlock::Type::FLOAT }, // offset = 32
{ "f2a", 0, UniformInterfaceBlock::Type::FLOAT2 }, // offset = 36
{ "f4a", 0, BufferInterfaceBlock::Type::FLOAT4 }, // offset = 0
{ "f4b", 0, BufferInterfaceBlock::Type::FLOAT4 }, // offset = 16
{ "f1a", 0, BufferInterfaceBlock::Type::FLOAT }, // offset = 32
{ "f2a", 0, BufferInterfaceBlock::Type::FLOAT2 }, // offset = 36
});
UniformInterfaceBlock uib(b.build());
BufferInterfaceBlock uib(b.build());
UniformBuffer buffer(uib.getSize());
float4 f4(1.0f);
ssize_t f4_offset = uib.getUniformOffset("f4a", 0);
ssize_t f4_offset = uib.getFieldOffset("f4a", 0);
buffer.setUniformArray(f4_offset, &f4, 1);
float2 f2(1.0f);
ssize_t f2_offset = uib.getUniformOffset("f2a", 0);
ssize_t f2_offset = uib.getFieldOffset("f2a", 0);
buffer.setUniformArray(f2_offset, &f2, 1);
buffer.invalidate();

View File

@@ -11,7 +11,7 @@ file(GLOB_RECURSE PUBLIC_HDRS ${PUBLIC_HDR_DIR}/**/*.h)
set(SRCS
src/SamplerInterfaceBlock.cpp
src/UniformInterfaceBlock.cpp
src/BufferInterfaceBlock.cpp
src/Variant.cpp
)

View File

@@ -160,6 +160,7 @@ static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8;
enum class MaterialDomain : uint8_t {
SURFACE = 0, //!< shaders applied to renderables
POST_PROCESS = 1, //!< shaders applied to rendered buffers
COMPUTE = 2, //!< compute shader
};
/**

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.
*/
#ifndef TNT_FILAMENT_DRIVER_BUFFERINTERFACEBLOCK_H
#define TNT_FILAMENT_DRIVER_BUFFERINTERFACEBLOCK_H
#include <backend/DriverEnums.h>
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <math/vec4.h>
#include <initializer_list>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <assert.h>
namespace filament {
class BufferInterfaceBlock {
public:
struct InterfaceBlockEntry {
std::string_view name;
uint32_t size;
backend::UniformType type;
backend::Precision precision{};
std::string_view structName{};
uint32_t stride{};
std::string_view sizeName{};
};
BufferInterfaceBlock();
BufferInterfaceBlock(const BufferInterfaceBlock& rhs) = delete;
BufferInterfaceBlock& operator=(const BufferInterfaceBlock& rhs) = delete;
BufferInterfaceBlock(BufferInterfaceBlock&& rhs) noexcept;
BufferInterfaceBlock& operator=(BufferInterfaceBlock&& rhs) noexcept;
~BufferInterfaceBlock() noexcept;
using Type = backend::UniformType;
using Precision = backend::Precision;
struct FieldInfo {
utils::CString name; // name of this field
uint16_t offset; // offset in "uint32_t" of this field in the buffer
uint8_t stride; // stride in "uint32_t" to the next element
Type type; // type of this field
bool isArray; // true if the field is an array
uint32_t size; // size of the array in elements, or 0 if not an array
Precision precision; // precision of this field
utils::CString structName; // name of this field structure if type is STRUCT
utils::CString sizeName; // name of the size parameter in the shader
// returns offset in bytes of this field (at index if an array)
inline size_t getBufferOffset(size_t index = 0) const {
assert_invariant(index < std::max(1u, size));
return (offset + stride * index) * sizeof(uint32_t);
}
};
enum class Alignment : uint8_t {
std140,
std430
};
enum class Target : uint8_t {
UNIFORM,
SSBO
};
enum class Qualifier : uint8_t {
COHERENT = 0x01,
WRITEONLY = 0x02,
READONLY = 0x04,
VOLATILE = 0x08,
RESTRICT = 0x10
};
class Builder {
public:
Builder() noexcept;
~Builder() noexcept;
Builder(Builder const& rhs) = default;
Builder(Builder&& rhs) noexcept = default;
Builder& operator=(Builder const& rhs) = default;
Builder& operator=(Builder&& rhs) noexcept = default;
// Give a name to this buffer interface block
Builder& name(std::string_view interfaceBlockName);
// Buffer target
Builder& target(Target target);
// build and return the BufferInterfaceBlock
Builder& alignment(Alignment alignment);
// add a qualifier
Builder& qualifier(Qualifier qualifier);
// a list of this buffer's fields
Builder& add(std::initializer_list<InterfaceBlockEntry> list);
// add a variable-sized array. must be the last entry.
Builder& addVariableSizedArray(InterfaceBlockEntry const& item);
BufferInterfaceBlock build();
bool hasVariableSizeArray() const;
private:
friend class BufferInterfaceBlock;
utils::CString mName;
std::vector<FieldInfo> mEntries;
Alignment mAlignment = Alignment::std140;
Target mTarget = Target::UNIFORM;
uint8_t mQualifiers = 0;
bool mHasVariableSizeArray = false;
};
// name of this BufferInterfaceBlock interface block
std::string_view getName() const noexcept { return { mName.data(), mName.size() }; }
// size needed for the buffer in bytes
size_t getSize() const noexcept { return mSize; }
// list of information records for each field
utils::FixedCapacityVector<FieldInfo> const& getFieldInfoList() const noexcept {
return mFieldInfoList;
}
// negative value if name doesn't exist or Panic if exceptions are enabled
ssize_t getFieldOffset(std::string_view name, size_t index) const;
FieldInfo const* getFieldInfo(std::string_view name) const;
bool hasField(std::string_view name) const noexcept {
return mInfoMap.find(name) != mInfoMap.end();
}
bool isEmpty() const noexcept { return mFieldInfoList.empty(); }
Alignment getAlignment() const noexcept { return mAlignment; }
Target getTarget() const noexcept { return mTarget; }
uint8_t getQualifier() const noexcept { return mQualifiers; }
private:
friend class Builder;
explicit BufferInterfaceBlock(Builder const& builder) noexcept;
static uint8_t baseAlignmentForType(Type type) noexcept;
static uint8_t strideForType(Type type, uint32_t stride) noexcept;
utils::CString mName;
utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
std::unordered_map<std::string_view , uint32_t> mInfoMap;
uint32_t mSize = 0; // size in bytes rounded to multiple of 4
Alignment mAlignment = Alignment::std140;
Target mTarget = Target::UNIFORM;
uint8_t mQualifiers = 0;
};
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_BUFFERINTERFACEBLOCK_H

View File

@@ -62,6 +62,11 @@ public:
Builder();
~Builder() noexcept;
Builder(Builder const& rhs) = default;
Builder(Builder&& rhs) noexcept = default;
Builder& operator=(Builder const& rhs) = default;
Builder& operator=(Builder&& rhs) noexcept = default;
struct ListEntry { // NOLINT(cppcoreguidelines-pro-type-member-init)
std::string_view name; // name of this sampler
Type type; // type of this sampler

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.
*/
#ifndef TNT_FILAMENT_DRIVER_UNIFORMINTERFACEBLOCK_H
#define TNT_FILAMENT_DRIVER_UNIFORMINTERFACEBLOCK_H
#include <backend/DriverEnums.h>
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <math/vec4.h>
#include <initializer_list>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <assert.h>
namespace filament {
class UniformInterfaceBlock {
public:
struct UniformBlockEntry {
std::string_view name;
uint32_t size;
backend::UniformType type;
backend::Precision precision{};
std::string_view structName{};
uint32_t stride{};
std::string_view sizeName{};
};
UniformInterfaceBlock();
UniformInterfaceBlock(const UniformInterfaceBlock& rhs) = delete;
UniformInterfaceBlock& operator=(const UniformInterfaceBlock& rhs) = delete;
UniformInterfaceBlock(UniformInterfaceBlock&& rhs) noexcept;
UniformInterfaceBlock& operator=(UniformInterfaceBlock&& rhs) noexcept;
~UniformInterfaceBlock() noexcept;
using Type = backend::UniformType;
using Precision = backend::Precision;
struct UniformInfo {
utils::CString name; // name of this uniform
uint16_t offset; // offset in "uint32_t" of this uniform in the buffer
uint8_t stride; // stride in "uint32_t" to the next element
Type type; // type of this uniform
uint32_t size; // size of the array in elements, or 0 if not an array
Precision precision; // precision of this uniform
utils::CString structName; // name of this uniform structure if type is STRUCT
utils::CString sizeName; // name of the size parameter in the shader
// returns offset in bytes of this uniform (at index if an array)
inline size_t getBufferOffset(size_t index = 0) const {
assert_invariant(index < std::max(1u, size));
return (offset + stride * index) * sizeof(uint32_t);
}
};
class Builder {
public:
Builder() noexcept;
~Builder() noexcept;
// Give a name to this uniform interface block
Builder& name(std::string_view interfaceBlockName);
// a list of uniform fields
Builder& add(std::initializer_list<UniformBlockEntry> list);
// build and return the UniformInterfaceBlock
UniformInterfaceBlock build();
private:
friend class UniformInterfaceBlock;
utils::CString mName;
std::vector<UniformInfo> mEntries;
};
// name of this uniform interface block
std::string_view getName() const noexcept { return { mName.data(), mName.size() }; }
// size in bytes needed to store the uniforms described by this interface block in a UniformBuffer
size_t getSize() const noexcept { return mSize; }
// list of information records for each uniform
utils::FixedCapacityVector<UniformInfo> const& getUniformInfoList() const noexcept {
return mUniformsInfoList;
}
// negative value if name doesn't exist or Panic if exceptions are enabled
ssize_t getUniformOffset(std::string_view name, size_t index) const;
UniformInfo const* getUniformInfo(std::string_view name) const;
bool hasUniform(std::string_view name) const noexcept {
return mInfoMap.find(name) != mInfoMap.end();
}
bool isEmpty() const noexcept { return mUniformsInfoList.empty(); }
private:
friend class Builder;
explicit UniformInterfaceBlock(Builder const& builder) noexcept;
static uint8_t baseAlignmentForType(Type type) noexcept;
static uint8_t strideForType(Type type, uint32_t stride) noexcept;
utils::CString mName;
utils::FixedCapacityVector<UniformInfo> mUniformsInfoList;
std::unordered_map<std::string_view , uint32_t> mInfoMap;
uint32_t mSize = 0; // size in bytes rounded to multiple of 4
};
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_UNIFORMINTERFACEBLOCK_H

View File

@@ -0,0 +1,233 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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 "private/filament/BufferInterfaceBlock.h"
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utility>
using namespace utils;
namespace filament {
BufferInterfaceBlock::Builder::Builder() noexcept = default;
BufferInterfaceBlock::Builder::~Builder() noexcept = default;
BufferInterfaceBlock::Builder&
BufferInterfaceBlock::Builder::name(std::string_view interfaceBlockName) {
mName = { interfaceBlockName.data(), interfaceBlockName.size() };
return *this;
}
BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::alignment(
BufferInterfaceBlock::Alignment alignment) {
mAlignment = alignment;
return *this;
}
BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::target(
BufferInterfaceBlock::Target target) {
mTarget = target;
return *this;
}
BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::qualifier(
BufferInterfaceBlock::Qualifier qualifier) {
mQualifiers |= uint8_t(qualifier);
return *this;
}
BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::add(
std::initializer_list<InterfaceBlockEntry> list) {
mEntries.reserve(mEntries.size() + list.size());
for (auto const& item : list) {
mEntries.push_back({
{ item.name.data(), item.name.size() },
0, uint8_t(item.stride), item.type, item.size > 0, item.size, item.precision,
{ item.structName.data(), item.structName.size() },
{ item.sizeName.data(), item.sizeName.size() }
});
}
return *this;
}
BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::addVariableSizedArray(
BufferInterfaceBlock::InterfaceBlockEntry const& item) {
mHasVariableSizeArray = true;
mEntries.push_back({
{ item.name.data(), item.name.size() },
0, uint8_t(item.stride), item.type, true, 0, item.precision,
{ item.structName.data(), item.structName.size() },
{ item.sizeName.data(), item.sizeName.size() }
});
return *this;
}
BufferInterfaceBlock BufferInterfaceBlock::Builder::build() {
// look for the first variable-size array
auto pos = std::find_if(mEntries.begin(), mEntries.end(),
[](FieldInfo const& item) -> bool {
return item.isArray && !item.size;
});
// if there is one, check it's the last entry
ASSERT_PRECONDITION(pos == mEntries.end() || pos == mEntries.end() - 1,
"the variable-size array must be the last entry");
// if we have a variable size array, we can't be a UBO
ASSERT_PRECONDITION(pos == mEntries.end() || mTarget == Target::SSBO,
"variable size arrays not supported for UBOs");
// std430 not available for UBOs
ASSERT_PRECONDITION(mAlignment == Alignment::std140 || mTarget == Target::SSBO,
"UBOs must use std140");
return BufferInterfaceBlock(*this);
}
bool BufferInterfaceBlock::Builder::hasVariableSizeArray() const {
return mHasVariableSizeArray;
}
// --------------------------------------------------------------------------------------------------------------------
BufferInterfaceBlock::BufferInterfaceBlock() = default;
BufferInterfaceBlock::BufferInterfaceBlock(BufferInterfaceBlock&& rhs) noexcept = default;
BufferInterfaceBlock& BufferInterfaceBlock::operator=(BufferInterfaceBlock&& rhs) noexcept = default;
BufferInterfaceBlock::~BufferInterfaceBlock() noexcept = default;
BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
: mName(builder.mName),
mFieldInfoList(builder.mEntries.size()),
mAlignment(builder.mAlignment),
mTarget(builder.mTarget)
{
auto& infoMap = mInfoMap;
infoMap.reserve(builder.mEntries.size());
auto& uniformsInfoList = mFieldInfoList;
uint32_t i = 0;
uint16_t offset = 0;
for (auto const& e : builder.mEntries) {
size_t alignment = baseAlignmentForType(e.type);
size_t stride = strideForType(e.type, e.stride);
if (e.isArray) { // this is an array
if (builder.mAlignment == Alignment::std140) {
// in std140 arrays are aligned to float4
alignment = 4;
}
// the stride of an array is always rounded to its alignment (which is POT)
stride = (stride + alignment - 1) & ~(alignment - 1);
}
// calculate the offset for this uniform
size_t padding = (alignment - (offset % alignment)) % alignment;
offset += padding;
FieldInfo& info = uniformsInfoList[i];
info = { e.name, offset, uint8_t(stride), e.type, e.isArray, e.size,
e.precision, e.structName, e.sizeName };
// record this uniform info
infoMap[{ info.name.data(), info.name.size() }] = i;
// advance offset to next slot
offset += stride * std::max(1u, e.size);
++i;
}
// round size to the next multiple of 4 and convert to bytes
mSize = sizeof(uint32_t) * ((offset + 3) & ~3);
}
ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index) const {
auto const* info = getFieldInfo(name);
assert_invariant(info);
return (ssize_t)info->getBufferOffset(index);
}
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
std::string_view name) const {
auto pos = mInfoMap.find(name);
ASSERT_PRECONDITION(pos != mInfoMap.end(),
"uniform named \"%.*s\" not found", name.size(), name.data());
return &mFieldInfoList[pos->second];
}
uint8_t UTILS_NOINLINE BufferInterfaceBlock::baseAlignmentForType(BufferInterfaceBlock::Type type) noexcept {
switch (type) {
case Type::BOOL:
case Type::FLOAT:
case Type::INT:
case Type::UINT:
return 1;
case Type::BOOL2:
case Type::FLOAT2:
case Type::INT2:
case Type::UINT2:
return 2;
case Type::BOOL3:
case Type::BOOL4:
case Type::FLOAT3:
case Type::FLOAT4:
case Type::INT3:
case Type::INT4:
case Type::UINT3:
case Type::UINT4:
case Type::MAT3:
case Type::MAT4:
case Type::STRUCT:
return 4;
}
}
uint8_t UTILS_NOINLINE BufferInterfaceBlock::strideForType(BufferInterfaceBlock::Type type, uint32_t stride) noexcept {
switch (type) {
case Type::BOOL:
case Type::INT:
case Type::UINT:
case Type::FLOAT:
return 1;
case Type::BOOL2:
case Type::INT2:
case Type::UINT2:
case Type::FLOAT2:
return 2;
case Type::BOOL3:
case Type::INT3:
case Type::UINT3:
case Type::FLOAT3:
return 3;
case Type::BOOL4:
case Type::INT4:
case Type::UINT4:
case Type::FLOAT4:
return 4;
case Type::MAT3:
return 12;
case Type::MAT4:
return 16;
case Type::STRUCT:
return stride;
}
}
} // namespace filament

View File

@@ -1,173 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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 "private/filament/UniformInterfaceBlock.h"
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utility>
using namespace utils;
namespace filament {
UniformInterfaceBlock::Builder::Builder() noexcept = default;
UniformInterfaceBlock::Builder::~Builder() noexcept = default;
UniformInterfaceBlock::Builder&
UniformInterfaceBlock::Builder::name(std::string_view interfaceBlockName) {
mName = { interfaceBlockName.data(), interfaceBlockName.size() };
return *this;
}
UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
std::initializer_list<UniformBlockEntry> list) {
mEntries.reserve(mEntries.size() + list.size());
for (auto const& item : list) {
mEntries.push_back({
{ item.name.data(), item.name.size() },
0, uint8_t(item.stride), item.type, item.size, item.precision,
{ item.structName.data(), item.structName.size() },
{ item.sizeName.data(), item.sizeName.size() }
});
}
return *this;
}
UniformInterfaceBlock UniformInterfaceBlock::Builder::build() {
return UniformInterfaceBlock(*this);
}
// --------------------------------------------------------------------------------------------------------------------
UniformInterfaceBlock::UniformInterfaceBlock() = default;
UniformInterfaceBlock::UniformInterfaceBlock(UniformInterfaceBlock&& rhs) noexcept = default;
UniformInterfaceBlock& UniformInterfaceBlock::operator=(UniformInterfaceBlock&& rhs) noexcept = default;
UniformInterfaceBlock::~UniformInterfaceBlock() noexcept = default;
UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
: mName(builder.mName), mUniformsInfoList(builder.mEntries.size())
{
auto& infoMap = mInfoMap;
infoMap.reserve(builder.mEntries.size());
auto& uniformsInfoList = mUniformsInfoList;
uint32_t i = 0;
uint16_t offset = 0;
for (auto const& e : builder.mEntries) {
size_t alignment = baseAlignmentForType(e.type);
uint8_t stride = strideForType(e.type, e.stride);
if (e.size > 0) { // this is an array
// round the alignment up to that of a float4
alignment = (alignment + 3) & ~3;
stride = (stride + uint8_t(3)) & ~uint8_t(3);
}
// calculate the offset for this uniform
size_t padding = (alignment - (offset % alignment)) % alignment;
offset += padding;
UniformInfo& info = uniformsInfoList[i];
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName, e.sizeName };
// record this uniform info
infoMap[{ info.name.data(), info.name.size() }] = i;
// advance offset to next slot
offset += stride * std::max(1u, e.size);
++i;
}
// round size to the next multiple of 4 and convert to bytes
mSize = sizeof(uint32_t) * ((offset + 3) & ~3);
}
ssize_t UniformInterfaceBlock::getUniformOffset(std::string_view name, size_t index) const {
auto const* info = getUniformInfo(name);
assert_invariant(info);
return (ssize_t)info->getBufferOffset(index);
}
UniformInterfaceBlock::UniformInfo const* UniformInterfaceBlock::getUniformInfo(
std::string_view name) const {
auto pos = mInfoMap.find(name);
ASSERT_PRECONDITION(pos != mInfoMap.end(),
"uniform named \"%.*s\" not found", name.size(), name.data());
return &mUniformsInfoList[pos->second];
}
uint8_t UTILS_NOINLINE UniformInterfaceBlock::baseAlignmentForType(UniformInterfaceBlock::Type type) noexcept {
switch (type) {
case Type::BOOL:
case Type::FLOAT:
case Type::INT:
case Type::UINT:
return 1;
case Type::BOOL2:
case Type::FLOAT2:
case Type::INT2:
case Type::UINT2:
return 2;
case Type::BOOL3:
case Type::BOOL4:
case Type::FLOAT3:
case Type::FLOAT4:
case Type::INT3:
case Type::INT4:
case Type::UINT3:
case Type::UINT4:
case Type::MAT3:
case Type::MAT4:
case Type::STRUCT:
return 4;
}
}
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type, uint32_t stride) noexcept {
switch (type) {
case Type::BOOL:
case Type::INT:
case Type::UINT:
case Type::FLOAT:
return 1;
case Type::BOOL2:
case Type::INT2:
case Type::UINT2:
case Type::FLOAT2:
return 2;
case Type::BOOL3:
case Type::INT3:
case Type::UINT3:
case Type::FLOAT3:
return 3;
case Type::BOOL4:
case Type::INT4:
case Type::UINT4:
case Type::FLOAT4:
return 4;
case Type::MAT3:
return 12;
case Type::MAT4:
return 16;
case Type::STRUCT:
return stride;
}
}
} // namespace filament

View File

@@ -66,13 +66,13 @@ public:
return { it->first, it->second };
}
const uint8_t* getChunkStart(Type type) const noexcept {
return mChunks.at(type).start;
}
const uint8_t* getChunkEnd(Type type) const noexcept {
ChunkDesc const& chunkDesc = mChunks.at(type);
return chunkDesc.start + chunkDesc.size;
std::pair<uint8_t const*, uint8_t const*> getChunkRange(Type type) const noexcept {
ChunkDesc const* pChunkDesc;
bool success = hasChunk(type, &pChunkDesc);
if (success) {
return { pChunkDesc->start, pChunkDesc->start + pChunkDesc->size };
}
return { nullptr, nullptr };
}
bool hasChunk(Type type, ChunkDesc const** pChunkDesc = nullptr) const noexcept {

View File

@@ -52,7 +52,7 @@ public:
private:
ChunkContainer const& mContainer;
filamat::ChunkType mMaterialTag = filamat::ChunkType::Unknown;
Unflattener mUnflattener{nullptr, nullptr};
Unflattener mUnflattener;
const uint8_t* mBase = nullptr;
tsl::robin_map<uint32_t, uint32_t> mOffsets;

View File

@@ -31,13 +31,16 @@ namespace filaflat {
// never assume a read will succeed.
class UTILS_PUBLIC Unflattener {
public:
Unflattener() noexcept = default;
Unflattener(const uint8_t* src, const uint8_t* end)
: mSrc(src), mCursor(src), mEnd(end) {
assert_invariant(src && end);
}
Unflattener(Unflattener const&) = default;
Unflattener(Unflattener const& rhs) = default;
~Unflattener() = default;
~Unflattener() noexcept = default;
bool hasData() const noexcept {
return mCursor < mEnd;
@@ -150,9 +153,9 @@ public:
}
private:
const uint8_t* mSrc;
const uint8_t* mCursor;
const uint8_t* mEnd;
const uint8_t* mSrc = nullptr;
const uint8_t* mCursor = nullptr;
const uint8_t* mEnd = nullptr;
};
} //namespace filaflat

View File

@@ -34,9 +34,8 @@ bool DictionaryReader::unflatten(ChunkContainer const& container,
ChunkContainer::Type dictionaryTag,
BlobDictionary& dictionary) {
Unflattener unflattener(
container.getChunkStart(dictionaryTag),
container.getChunkEnd(dictionaryTag));
auto [start, end] = container.getChunkRange(dictionaryTag);
Unflattener unflattener(start, end);
if (dictionaryTag == ChunkType::DictionarySpirv) {
uint32_t compressionScheme;

View File

@@ -46,9 +46,8 @@ bool MaterialChunk::initialize(filamat::ChunkType materialTag) {
return true;
}
Unflattener unflattener(
mContainer.getChunkStart(materialTag),
mContainer.getChunkEnd(materialTag));
auto [start, end] = mContainer.getChunkRange(materialTag);
Unflattener unflattener(start, end);
mUnflattener = unflattener;
mMaterialTag = materialTag;

View File

@@ -14,6 +14,7 @@ set(PUBLIC_HDR_DIR include)
# ==================================================================================================
set(HDRS
include/filamat/Enums.h
include/filamat/IncludeCallback.h
include/filamat/MaterialBuilder.h
include/filamat/Package.h)

View File

@@ -19,14 +19,6 @@
#ifndef TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#define TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#include <cstddef>
#include <cstdint>
#include <atomic>
#include <string>
#include <utility>
#include <vector>
#include <filament/MaterialEnums.h>
#include <filamat/IncludeCallback.h>
@@ -40,10 +32,24 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <math/vec3.h>
#include <atomic>
#include <string>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class JobSystem;
}
namespace filament {
class BufferInterfaceBlock;
}
namespace filamat {
struct MaterialInfo;
@@ -136,10 +142,10 @@ protected:
std::vector<CodeGenParams> mCodeGenPermutations;
// For finding properties and running semantic analysis, we always use the same code gen
// permutation. This is the first permutation generated with default arguments passed to matc.
const CodeGenParams mSemanticCodeGenParams = {
.shaderModel = ShaderModel::MOBILE,
.targetApi = TargetApi::OPENGL,
.targetLanguage = TargetLanguage::SPIRV
static constexpr const CodeGenParams mSemanticCodeGenParams = {
.shaderModel = ShaderModel::MOBILE,
.targetApi = TargetApi::OPENGL,
.targetLanguage = TargetLanguage::SPIRV
};
// Keeps track of how many times MaterialBuilder::init() has been called without a call to
@@ -151,7 +157,7 @@ protected:
// Utility function that looks at an Engine backend to determine TargetApi
inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend(
filament::backend::Backend backend) noexcept {
filament::backend::Backend backend) noexcept {
using filament::backend::Backend;
using TargetApi = MaterialBuilderBase::TargetApi;
switch (backend) {
@@ -199,6 +205,13 @@ inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend(
class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase {
public:
MaterialBuilder();
~MaterialBuilder();
MaterialBuilder(const MaterialBuilder& rhs) = delete;
MaterialBuilder& operator=(const MaterialBuilder& rhs) = delete;
MaterialBuilder(MaterialBuilder&& rhs) noexcept = default;
MaterialBuilder& operator=(MaterialBuilder&& rhs) noexcept = default;
static constexpr size_t MATERIAL_VARIABLES_COUNT = 4;
enum class Variable : uint8_t {
@@ -252,7 +265,7 @@ public:
std::string value;
PreprocessorDefine(std::string name, std::string value) :
name(std::move(name)), value(std::move(value)) {}
name(std::move(name)), value(std::move(value)) {}
};
using PreprocessorDefineList = std::vector<PreprocessorDefine>;
@@ -269,38 +282,28 @@ public:
MaterialBuilder& interpolation(Interpolation interpolation) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, ParameterPrecision precision,
const char* name) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, const char* name) noexcept {
return parameter(type, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& parameter(const char* name, UniformType type,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size,
ParameterPrecision precision, const char* name) noexcept;
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size, const char* name) noexcept {
return parameter(type, size, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
/**
* Add a sampler parameter to this material.
*
* When SamplerType::SAMPLER_EXTERNAL is specifed, format and precision are ignored.
* When SamplerType::SAMPLER_EXTERNAL is specified, format and precision are ignored.
*/
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
ParameterPrecision precision, const char* name) noexcept;
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, ParameterPrecision precision,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, const char* name) noexcept;
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
@@ -313,7 +316,7 @@ public:
MaterialBuilder& require(VertexAttribute attribute) noexcept;
//! Specify the domain that this material will operate in.
MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept;
MaterialBuilder& materialDomain(MaterialBuilder::MaterialDomain materialDomain) noexcept;
/**
* Set the code content of this material.
@@ -565,6 +568,9 @@ public:
*/
MaterialBuilder& useLegacyMorphing() noexcept;
//! specify compute kernel group size
MaterialBuilder& groupSize(filament::math::uint3 groupSize) noexcept;
/**
* Build the material. If you are using the Filament engine with this library, you should use
* the job system provided by Engine.
@@ -578,16 +584,16 @@ public:
/**
* Add a subpass parameter to this material.
*/
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, ParameterPrecision
precision, const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name)
noexcept;
MaterialBuilder& parameter(SubpassType subpassType, ParameterPrecision precision,
const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept;
MaterialBuilder& subpass(SubpassType subpassType,
SamplerFormat format, ParameterPrecision precision, const char* name) noexcept;
MaterialBuilder& subpass(SubpassType subpassType,
SamplerFormat format, const char* name) noexcept;
MaterialBuilder& subpass(SubpassType subpassType,
ParameterPrecision precision, const char* name) noexcept;
MaterialBuilder& subpass(SubpassType subpassType, const char* name) noexcept;
struct Parameter {
Parameter() noexcept : parameterType(INVALID) {}
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
@@ -624,8 +630,8 @@ public:
Output() noexcept = default;
Output(const char* outputName, VariableQualifier qualifier, OutputTarget target,
OutputType type, int location) noexcept
: name(outputName), qualifier(qualifier), target(target), type(type),
location(location) { }
: name(outputName), qualifier(qualifier), target(target), type(type),
location(location) { }
utils::CString name;
VariableQualifier qualifier;
@@ -657,7 +663,10 @@ public:
static constexpr size_t MAX_PARAMETERS_COUNT = 48;
static constexpr size_t MAX_SUBPASS_COUNT = 1;
static constexpr size_t MAX_BUFFERS_COUNT = 4;
using ParameterList = Parameter[MAX_PARAMETERS_COUNT];
using SubpassList = Parameter[MAX_SUBPASS_COUNT];
using BufferList = std::vector<std::unique_ptr<filament::BufferInterfaceBlock>>;
// returns the number of parameters declared in this material
uint8_t getParameterCount() const noexcept { return mParameterCount; }
@@ -665,6 +674,12 @@ public:
// returns a list of at least getParameterCount() parameters
const ParameterList& getParameters() const noexcept { return mParameters; }
// returns the number of parameters declared in this material
uint8_t getSubpassCount() const noexcept { return mSubpassCount; }
// returns a list of at least getParameterCount() parameters
const SubpassList& getSubPasses() const noexcept { return mSubpasses; }
filament::UserVariantFilterMask getVariantFilter() const { return mVariantFilter; }
/// @endcond
@@ -735,8 +750,10 @@ private:
PropertyList mProperties;
ParameterList mParameters;
SubpassList mSubpasses;
VariableList mVariables;
OutputList mOutputs;
BufferList mBuffers;
ShaderQuality mShaderQuality = ShaderQuality::DEFAULT;
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
@@ -758,10 +775,13 @@ private:
float mSpecularAntiAliasingVariance = 0.15f;
float mSpecularAntiAliasingThreshold = 0.2f;
filament::math::uint3 mGroupSize = { 1, 1, 1 };
bool mShadowMultiplier = false;
bool mTransparentShadow = false;
uint8_t mParameterCount = 0;
uint8_t mSubpassCount = 0;
bool mDoubleSided = false;
bool mDoubleSidedCapability = false;

View File

@@ -77,6 +77,7 @@ static BindingIndexMap getBindingIndexMap(const GLSLPostProcessor::Config& confi
}
}
case MaterialDomain::POST_PROCESS:
case MaterialDomain::COMPUTE:
break;
}
generateBindingIndexMap(config, config.materialInfo->sib, map);
@@ -98,15 +99,6 @@ GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization,
GLSLPostProcessor::~GLSLPostProcessor() = default;
static uint32_t glslVersionFromShaderModel(ShaderModel model) {
switch (model) {
case ShaderModel::MOBILE:
return 300;
case ShaderModel::DESKTOP:
return 410;
}
}
static bool filterSpvOptimizerMessage(spv_message_level_t level) {
#ifdef NDEBUG
// In release builds, only log errors.
@@ -238,6 +230,9 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
case ShaderStage::FRAGMENT:
internalConfig.shLang = EShLangFragment;
break;
case ShaderStage::COMPUTE:
internalConfig.shLang = EShLangCompute;
break;
}
TProgram program;
@@ -249,7 +244,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
const char* shaderCString = inputShader.c_str();
tShader.setStrings(&shaderCString, 1);
internalConfig.langVersion = GLSLTools::glslangVersionFromShaderModel(config.shaderModel);
internalConfig.langVersion = GLSLTools::getGlslDefaultVersion(config.shaderModel);
GLSLTools::prepareShaderParser(config.targetApi, config.targetLanguage, tShader,
internalConfig.shLang, internalConfig.langVersion);
@@ -333,7 +328,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
std::string glsl;
TShader::ForbidIncluder forbidIncluder;
const int version = GLSLTools::glslangVersionFromShaderModel(config.shaderModel);
const int version = GLSLTools::getGlslDefaultVersion(config.shaderModel);
EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(config.targetApi, config.targetLanguage);
bool ok = tShader.preprocess(&DefaultTBuiltInResource, version, ENoProfile, false, false,
msg, &glsl, forbidIncluder);
@@ -403,15 +398,17 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
// Transpile back to GLSL
if (internalConfig.glslOutput) {
CompilerGLSL::Options glslOptions;
glslOptions.es = config.shaderModel == ShaderModel::MOBILE;
glslOptions.version = glslVersionFromShaderModel(config.shaderModel);
auto version = GLSLTools::getShadingLanguageVersion(
config.shaderModel, config.featureLevel);
glslOptions.es = version.second;
glslOptions.version = version.first;
glslOptions.enable_420pack_extension = glslOptions.version >= 420;
glslOptions.fragment.default_float_precision = glslOptions.es ?
CompilerGLSL::Options::Precision::Mediump : CompilerGLSL::Options::Precision::Highp;
glslOptions.fragment.default_int_precision = glslOptions.es ?
CompilerGLSL::Options::Precision::Mediump : CompilerGLSL::Options::Precision::Highp;
CompilerGLSL glslCompiler(move(spirv));
CompilerGLSL glslCompiler(std::move(spirv));
glslCompiler.set_common_options(glslOptions);
if (!glslOptions.es) {

View File

@@ -55,6 +55,7 @@ public:
MaterialBuilder::TargetLanguage targetLanguage;
filament::backend::ShaderStage shaderType;
filament::backend::ShaderModel shaderModel;
filament::backend::FeatureLevel featureLevel;
filament::MaterialDomain domain;
const filamat::MaterialInfo* materialInfo;
bool hasFramebufferFetch;
@@ -74,7 +75,8 @@ private:
SpirvBlob* spirvOutput = nullptr;
std::string* mslOutput = nullptr;
EShLanguage shLang = EShLangFragment;
int langVersion = 0;
// use 100 for ES environment, 110 for desktop
int langVersion = 0;
ShaderMinifier minifier;
};

View File

@@ -16,24 +16,18 @@
#include "filamat/MaterialBuilder.h"
#include <atomic>
#include <utility>
#include <vector>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>
#include <filamat/Enums.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UibStructs.h>
#include "Includes.h"
#include "MaterialVariants.h"
#include "SibGenerator.h"
#include "MaterialVariants.h"
#ifndef FILAMAT_LITE
# include "GLSLPostProcessor.h"
# include "sca/GLSLTools.h"
#else
# include "sca/GLSLToolsLite.h"
#endif
#include "shaders/MaterialInfo.h"
#include "shaders/ShaderGenerator.h"
@@ -48,14 +42,18 @@
#include "eiff/DictionaryTextChunk.h"
#include "eiff/DictionarySpirvChunk.h"
#include "Includes.h"
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UibStructs.h>
#ifndef FILAMAT_LITE
#include "GLSLPostProcessor.h"
#include "sca/GLSLTools.h"
#else
#include "sca/GLSLToolsLite.h"
#endif
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>
#include <atomic>
#include <utility>
#include <vector>
namespace filamat {
@@ -67,7 +65,7 @@ std::atomic<int> MaterialBuilderBase::materialBuilderClients(0);
inline void assertSingleTargetApi(MaterialBuilderBase::TargetApi api) {
// Assert that a single bit is set.
UTILS_UNUSED uint8_t bits = (uint8_t) api;
UTILS_UNUSED uint8_t bits = (uint8_t)api;
assert(bits && !(bits & bits - 1u));
}
@@ -86,7 +84,7 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics) {
// OpenGL is a special case. If we're doing any optimization, then we need to go to Spir-V.
TargetLanguage glTargetLanguage = mOptimization > MaterialBuilder::Optimization::PREPROCESSOR ?
TargetLanguage::SPIRV : TargetLanguage::GLSL;
TargetLanguage::SPIRV : TargetLanguage::GLSL;
if (vulkanSemantics) {
// Currently GLSLPostProcessor.cpp is incapable of compiling SPIRV to GLSL without
// running the optimizer. For now we just activate the optimizer in that case.
@@ -123,6 +121,8 @@ MaterialBuilder::MaterialBuilder() : mMaterialName("Unnamed") {
mShaderModels.reset();
}
MaterialBuilder::~MaterialBuilder() = default;
void MaterialBuilderBase::init() {
materialBuilderClients++;
#ifndef FILAMAT_LITE
@@ -187,68 +187,59 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(
UniformType type, ParameterPrecision precision, const char* name) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, type, 1, precision };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(
UniformType type, size_t size, ParameterPrecision precision, const char* name) noexcept {
MaterialBuilder& MaterialBuilder::parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, type, size, precision };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(
SamplerType samplerType, SamplerFormat format, ParameterPrecision precision, const char* name) noexcept {
MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type,
ParameterPrecision precision) noexcept {
return parameter(name, 1, type, precision);
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, samplerType, format, precision };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, SamplerFormat format,
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept {
return parameter(name, samplerType, SamplerFormat::FLOAT, precision);
}
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) noexcept {
ASSERT_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT, "Too many buffers");
mBuffers.emplace_back(std::make_unique<filament::BufferInterfaceBlock>(std::move(bib)));
return *this;
}
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat format,
ParameterPrecision precision, const char* name) noexcept {
ASSERT_PRECONDITION(format == SamplerFormat::FLOAT,
"Subpass parameters must have FLOAT format.");
auto subpassCount = std::count_if(std::begin(mParameters), std::end(mParameters),
[](const auto& p) { return p.isSubpass(); });
ASSERT_POSTCONDITION(subpassCount < MAX_SUBPASS_COUNT, "Too many subpasses");
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, subpassType, format, precision };
ASSERT_POSTCONDITION(mSubpassCount < MAX_SUBPASS_COUNT, "Too many subpasses");
mSubpasses[mSubpassCount++] = { name, subpassType, format, precision };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(
SamplerType samplerType, SamplerFormat format, const char* name) noexcept {
return parameter(samplerType, format, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& MaterialBuilder::parameter(
SamplerType samplerType, ParameterPrecision precision, const char* name) noexcept {
return parameter(samplerType, SamplerFormat::FLOAT, precision, name);
}
MaterialBuilder& MaterialBuilder::parameter(
SamplerType samplerType, const char* name) noexcept {
return parameter(samplerType, SamplerFormat::FLOAT, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, SamplerFormat format,
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat format,
const char* name) noexcept {
return parameter(subpassType, format, ParameterPrecision::DEFAULT, name);
return subpass(subpassType, format, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, ParameterPrecision precision,
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, ParameterPrecision precision,
const char* name) noexcept {
return parameter(subpassType, SamplerFormat::FLOAT, precision, name);
return subpass(subpassType, SamplerFormat::FLOAT, precision, name);
}
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, const char* name) noexcept {
return parameter(subpassType, SamplerFormat::FLOAT, ParameterPrecision::DEFAULT, name);
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, const char* name) noexcept {
return subpass(subpassType, SamplerFormat::FLOAT, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& MaterialBuilder::require(VertexAttribute attribute) noexcept {
@@ -256,8 +247,20 @@ MaterialBuilder& MaterialBuilder::require(VertexAttribute attribute) noexcept {
return *this;
}
MaterialBuilder& MaterialBuilder::materialDomain(MaterialDomain materialDomain) noexcept {
MaterialBuilder& MaterialBuilder::groupSize(filament::math::uint3 groupSize) noexcept {
mGroupSize = groupSize;
return *this;
}
MaterialBuilder& MaterialBuilder::materialDomain(
MaterialBuilder::MaterialDomain materialDomain) noexcept {
mMaterialDomain = materialDomain;
if (mMaterialDomain == MaterialDomain::COMPUTE) {
// compute implies feature level 2
if (mFeatureLevel < FeatureLevel::FEATURE_LEVEL_2) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
}
}
return *this;
}
@@ -445,25 +448,34 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
// Build the per-material sampler block and uniform block.
SamplerInterfaceBlock::Builder sbb;
UniformInterfaceBlock::Builder ibb;
BufferInterfaceBlock::Builder ibb;
for (size_t i = 0, c = mParameterCount; i < c; i++) {
auto const& param = mParameters[i];
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
param.samplerType, param.format, param.precision);
} else if (param.isUniform()) {
ibb.add({{{ param.name.data(), param.name.size() },
uint32_t(param.size == 1u ? 0u : param.size), param.uniformType, param.precision }});
} else if (param.isSubpass()) {
// For now, we only support a single subpass for attachment 0.
// Subpasses belong to the "MaterialParams" block.
const uint8_t attachmentIndex = 0;
const uint8_t binding = 0;
info.subpass = { CString("MaterialParams"), param.name, param.subpassType,
param.format, param.precision, attachmentIndex, binding };
}
}
for (size_t i = 0, c = mSubpassCount; i < c; i++) {
auto const& param = mSubpasses[i];
assert_invariant(param.isSubpass());
// For now, we only support a single subpass for attachment 0.
// Subpasses belong to the "MaterialParams" block.
const uint8_t attachmentIndex = 0;
const uint8_t binding = 0;
info.subpass = { CString("MaterialParams"), param.name, param.subpassType,
param.format, param.precision, attachmentIndex, binding };
}
for (auto const& buffer : mBuffers) {
info.buffers.emplace_back(buffer.get());
}
if (mSpecularAntiAliasing) {
ibb.add({
{ "_specularAntiAliasingVariance", 0, UniformType::FLOAT },
@@ -512,6 +524,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
info.instanced = mInstanced;
info.vertexDomainDeviceJittered = mVertexDomainDeviceJittered;
info.featureLevel = mFeatureLevel;
info.groupSize = mGroupSize;
}
bool MaterialBuilder::findProperties(backend::ShaderStage type,
@@ -576,15 +589,22 @@ bool MaterialBuilder::runSemanticAnalysis(MaterialInfo const& info) noexcept {
targetApi = TargetApi::VULKAN;
}
ShaderModel model = static_cast<ShaderModel>(mSemanticCodeGenParams.shaderModel);
std::string shaderCode = peek(ShaderStage::VERTEX, mSemanticCodeGenParams, mProperties);
bool result = GLSLTools::analyzeVertexShader(shaderCode, model, mMaterialDomain,
targetApi, targetLanguage, info);
if (!result) return false;
shaderCode = peek(ShaderStage::FRAGMENT, mSemanticCodeGenParams, mProperties);
result = GLSLTools::analyzeFragmentShader(shaderCode, model, mMaterialDomain,
targetApi, targetLanguage, mCustomSurfaceShading, info);
bool result = false;
ShaderModel model = mSemanticCodeGenParams.shaderModel;
if (mMaterialDomain == filament::MaterialDomain::COMPUTE) {
std::string shaderCode = peek(ShaderStage::COMPUTE, mSemanticCodeGenParams, mProperties);
result = GLSLTools::analyzeComputeShader(shaderCode, model,
targetApi, targetLanguage, info);
} else {
std::string shaderCode = peek(ShaderStage::VERTEX, mSemanticCodeGenParams, mProperties);
result = GLSLTools::analyzeVertexShader(shaderCode, model, mMaterialDomain,
targetApi, targetLanguage, info);
if (result) {
shaderCode = peek(ShaderStage::FRAGMENT, mSemanticCodeGenParams, mProperties);
result = GLSLTools::analyzeFragmentShader(shaderCode, model, mMaterialDomain,
targetApi, targetLanguage, mCustomSurfaceShading, info);
}
}
return result;
#else
return true;
@@ -614,14 +634,14 @@ bool MaterialBuilder::ShaderCode::resolveIncludes(IncludeCallback callback,
const CString& fileName) noexcept {
if (!mCode.empty()) {
ResolveOptions options {
.insertLineDirectives = true,
.insertLineDirectiveCheck = true
.insertLineDirectives = true,
.insertLineDirectiveCheck = true
};
IncludeResult source {
.includeName = fileName,
.text = mCode,
.lineNumberOffset = getLineOffset(),
.name = CString("")
.includeName = fileName,
.text = mCode,
.lineNumberOffset = getLineOffset(),
.name = CString("")
};
if (!::filamat::resolveIncludes(source, std::move(callback), options)) {
return false;
@@ -734,19 +754,19 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
shader = sg.createVertexProgram(
shaderModel, targetApi, targetLanguage, info, v.variant,
mInterpolation, mVertexDomain);
#ifdef FILAMAT_LITE
GLSLToolsLite glslTools;
glslTools.removeGoogleLineDirectives(shader);
#endif
} else if (v.stage == backend::ShaderStage::FRAGMENT) {
shader = sg.createFragmentProgram(
shaderModel, targetApi, targetLanguage, info, v.variant, mInterpolation);
#ifdef FILAMAT_LITE
GLSLToolsLite glslTools;
glslTools.removeGoogleLineDirectives(shader);
#endif
} else if (v.stage == backend::ShaderStage::COMPUTE) {
shader = sg.createComputeProgram(
shaderModel, targetApi, targetLanguage, info, v.variant);
}
#ifdef FILAMAT_LITE
GLSLToolsLite glslTools;
glslTools.removeGoogleLineDirectives(shader);
#endif
std::string* pGlsl = nullptr;
if (targetApiNeedsGlsl) {
pGlsl = &shader;
@@ -759,6 +779,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
.targetLanguage = targetLanguage,
.shaderType = v.stage,
.shaderModel = shaderModel,
.featureLevel = mFeatureLevel,
.domain = mMaterialDomain,
.materialInfo = &info,
.hasFramebufferFetch = mEnableFramebufferFetch,
@@ -966,6 +987,8 @@ error:
output(VariableQualifier::OUT, OutputTarget::COLOR, OutputType::FLOAT4, "color");
}
// TODO: maybe check MaterialDomain::COMPUTE has outputs
// Resolve all the #include directives within user code.
if (!mMaterialFragmentCode.resolveIncludes(mIncludeCallback, mFileName) ||
!mMaterialVertexCode.resolveIncludes(mIncludeCallback, mFileName)) {
@@ -1016,9 +1039,20 @@ error:
info.useLegacyMorphing = mUseLegacyMorphing;
// Generate all shaders and write the shader chunks.
const auto variants = mMaterialDomain == MaterialDomain::SURFACE ?
determineSurfaceVariants(mVariantFilter, isLit(), mShadowMultiplier) :
determinePostProcessVariants();
std::vector<Variant> variants;
switch (mMaterialDomain) {
case MaterialDomain::SURFACE:
variants = determineSurfaceVariants(mVariantFilter, isLit(), mShadowMultiplier);
break;
case MaterialDomain::POST_PROCESS:
variants = determinePostProcessVariants();
break;
case MaterialDomain::COMPUTE:
variants = determineComputeVariants();
break;
}
success = generateShaders(jobSystem, variants, container, info);
if (!success) {
// Return an empty package to signal a failure to build the material.
@@ -1039,7 +1073,8 @@ static const char* to_string(ShaderStageFlags stageFlags) noexcept {
case ShaderStageFlags::NONE: return "{ }";
case ShaderStageFlags::VERTEX: return "{ vertex }";
case ShaderStageFlags::FRAGMENT: return "{ fragment }";
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS: return "{ vertex | fragment }";
case ShaderStageFlags::COMPUTE: return "{ compute }";
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS: return "{ vertex | fragment | COMPUTE }";
}
return nullptr;
}
@@ -1051,7 +1086,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
auto const* stage = to_string(sib.getStageFlags());
for (auto const& sampler: samplers) {
slog.e << "\"" << sampler.name.c_str() << "\" "
<< Enums::toString(sampler.type).c_str() << " " << stage << '\n';
<< Enums::toString(sampler.type).c_str() << " " << stage << '\n';
}
flush(slog.e);
};
@@ -1116,23 +1151,31 @@ bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
mBlendingMode == BlendingMode::FADE));
}
std::string MaterialBuilder::peek(backend::ShaderStage type,
std::string MaterialBuilder::peek(backend::ShaderStage stage,
const CodeGenParams& params, const PropertyList& properties) noexcept {
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines, mMaterialFragmentCode.getResolved(),
mMaterialFragmentCode.getLineOffset(), mMaterialVertexCode.getResolved(),
mMaterialVertexCode.getLineOffset(), mMaterialDomain);
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines,
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
mMaterialDomain);
MaterialInfo info;
prepareToBuild(info);
info.samplerBindings.init(mMaterialDomain, info.sib);
if (type == backend::ShaderStage::VERTEX) {
return sg.createVertexProgram(ShaderModel(params.shaderModel),
params.targetApi, params.targetLanguage, info, {}, mInterpolation, mVertexDomain);
} else {
return sg.createFragmentProgram(ShaderModel(params.shaderModel), params.targetApi,
params.targetLanguage, info, {}, mInterpolation);
switch (stage) {
case backend::ShaderStage::VERTEX:
return sg.createVertexProgram(
params.shaderModel, params.targetApi, params.targetLanguage,
info, {}, mInterpolation, mVertexDomain);
case backend::ShaderStage::FRAGMENT:
return sg.createFragmentProgram(
params.shaderModel, params.targetApi, params.targetLanguage,
info, {}, mInterpolation);
case backend::ShaderStage::COMPUTE:
return sg.createComputeProgram(
params.shaderModel, params.targetApi, params.targetLanguage,
info, {});
}
}
@@ -1143,19 +1186,22 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
container.addSimpleChild<uint32_t>(ChunkType::MaterialShaderModels, mShaderModels.getValue());
container.addSimpleChild<uint8_t>(ChunkType::MaterialDomain, static_cast<uint8_t>(mMaterialDomain));
// note: this chunk is only needed for OpenGL backends, which don't all support layout(binding=)
using namespace filament;
FixedCapacityVector<std::pair<std::string_view, UniformBindingPoints>> list = {
{ PerViewUib::_name, UniformBindingPoints::PER_VIEW },
{ PerRenderableUib::_name, UniformBindingPoints::PER_RENDERABLE },
{ LightsUib::_name, UniformBindingPoints::LIGHTS },
{ ShadowUib::_name, UniformBindingPoints::SHADOW },
{ FroxelRecordUib::_name, UniformBindingPoints::FROXEL_RECORDS },
{ PerRenderableBoneUib::_name, UniformBindingPoints::PER_RENDERABLE_BONES },
{ PerRenderableMorphingUib::_name, UniformBindingPoints::PER_RENDERABLE_MORPHING },
{ info.uib.getName(), UniformBindingPoints::PER_MATERIAL_INSTANCE }
};
container.addChild<MaterialUniformBlockBindingsChunk>(std::move(list));
if (info.featureLevel <= FeatureLevel::FEATURE_LEVEL_1) {
// note: this chunk is only needed for OpenGL backends, which don't all support layout(binding=)
FixedCapacityVector<std::pair<std::string_view, UniformBindingPoints>> list = {
{ PerViewUib::_name, UniformBindingPoints::PER_VIEW },
{ PerRenderableUib::_name, UniformBindingPoints::PER_RENDERABLE },
{ LightsUib::_name, UniformBindingPoints::LIGHTS },
{ ShadowUib::_name, UniformBindingPoints::SHADOW },
{ FroxelRecordUib::_name, UniformBindingPoints::FROXEL_RECORDS },
{ PerRenderableBoneUib::_name, UniformBindingPoints::PER_RENDERABLE_BONES },
{ PerRenderableMorphingUib::_name, UniformBindingPoints::PER_RENDERABLE_MORPHING },
{ info.uib.getName(), UniformBindingPoints::PER_MATERIAL_INSTANCE }
};
container.addChild<MaterialUniformBlockBindingsChunk>(std::move(list));
}
// note: this chunk is needed for Vulkan and GL backends. Metal shouldn't need it (but
// still does as of now).
@@ -1167,30 +1213,35 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
// User Material SIB
container.addChild<MaterialSamplerInterfaceBlockChunk>(info.sib);
// User Subpass
container.addChild<MaterialSubpassInterfaceBlockChunk>(info.subpass);
// TODO: should we write the SSBO info? this would only be needed if we wanted to provide
// an interface to set [get?] values in the buffer. But we can do that easily
// with a c-struct (what about kotlin/java?). tbd.
if (mMaterialDomain != MaterialDomain::COMPUTE) {
// User Subpass
container.addChild<MaterialSubpassInterfaceBlockChunk>(info.subpass);
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability);
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSided, mDoubleSided);
container.addSimpleChild<uint8_t>(ChunkType::MaterialBlendingMode, static_cast<uint8_t>(mBlendingMode));
container.addSimpleChild<uint8_t>(ChunkType::MaterialTransparencyMode, static_cast<uint8_t>(mTransparencyMode));
container.addSimpleChild<uint8_t>(ChunkType::MaterialReflectionMode, static_cast<uint8_t>(mReflectionMode));
container.addSimpleChild<bool>(ChunkType::MaterialDepthWriteSet, mDepthWriteSet);
container.addSimpleChild<bool>(ChunkType::MaterialColorWrite, mColorWrite);
container.addSimpleChild<bool>(ChunkType::MaterialDepthWrite, mDepthWrite);
container.addSimpleChild<bool>(ChunkType::MaterialDepthTest, mDepthTest);
container.addSimpleChild<bool>(ChunkType::MaterialInstanced, mInstanced);
container.addSimpleChild<uint8_t>(ChunkType::MaterialCullingMode, static_cast<uint8_t>(mCullingMode));
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability);
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSided, mDoubleSided);
container.addSimpleChild<uint8_t>(ChunkType::MaterialBlendingMode, static_cast<uint8_t>(mBlendingMode));
container.addSimpleChild<uint8_t>(ChunkType::MaterialTransparencyMode, static_cast<uint8_t>(mTransparencyMode));
container.addSimpleChild<uint8_t>(ChunkType::MaterialReflectionMode, static_cast<uint8_t>(mReflectionMode));
container.addSimpleChild<bool>(ChunkType::MaterialDepthWriteSet, mDepthWriteSet);
container.addSimpleChild<bool>(ChunkType::MaterialColorWrite, mColorWrite);
container.addSimpleChild<bool>(ChunkType::MaterialDepthWrite, mDepthWrite);
container.addSimpleChild<bool>(ChunkType::MaterialDepthTest, mDepthTest);
container.addSimpleChild<bool>(ChunkType::MaterialInstanced, mInstanced);
container.addSimpleChild<uint8_t>(ChunkType::MaterialCullingMode, static_cast<uint8_t>(mCullingMode));
uint64_t properties = 0;
UTILS_NOUNROLL
for (size_t i = 0; i < MATERIAL_PROPERTIES_COUNT; i++) {
if (mProperties[i]) {
properties |= uint64_t(1u) << i;
uint64_t properties = 0;
UTILS_NOUNROLL
for (size_t i = 0; i < MATERIAL_PROPERTIES_COUNT; i++) {
if (mProperties[i]) {
properties |= uint64_t(1u) << i;
}
}
container.addSimpleChild<uint64_t>(ChunkType::MaterialProperties, properties);
}
container.addSimpleChild<uint64_t>(ChunkType::MaterialProperties, properties);
}
void MaterialBuilder::writeSurfaceChunks(ChunkContainer& container) const noexcept {

View File

@@ -59,4 +59,12 @@ std::vector<Variant> determinePostProcessVariants() {
return variants;
}
std::vector<Variant> determineComputeVariants() {
// TODO: should we have variants for compute shaders?
std::vector<Variant> variants;
filament::Variant variant(0);
variants.emplace_back(variant, filament::backend::ShaderStage::COMPUTE);
return variants;
}
} // namespace filamat

View File

@@ -37,6 +37,8 @@ std::vector<Variant> determineSurfaceVariants(
std::vector<Variant> determinePostProcessVariants();
std::vector<Variant> determineComputeVariants();
} // namespace filamat
#endif

View File

@@ -77,6 +77,7 @@ void SamplerBindingMap::init(MaterialDomain materialDomain,
}
break;
case MaterialDomain::POST_PROCESS:
case MaterialDomain::COMPUTE:
processSamplerGroup(SamplerBindingPoints::PER_MATERIAL_INSTANCE);
break;
}

View File

@@ -17,7 +17,7 @@
#include "UibGenerator.h"
#include "private/filament/UibStructs.h"
#include "private/filament/UniformInterfaceBlock.h"
#include "private/filament/BufferInterfaceBlock.h"
#include <private/filament/EngineEnums.h>
#include <backend/DriverEnums.h>
@@ -29,177 +29,178 @@ using namespace backend;
static_assert(CONFIG_MAX_SHADOW_CASCADES == 4,
"Changing CONFIG_MAX_SHADOW_CASCADES affects PerView size and breaks materials.");
UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
using Type = BufferInterfaceBlock::Type;
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(PerViewUib::_name)
.add({
{ "viewFromWorldMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "worldFromViewMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "clipFromViewMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "viewFromClipMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "clipFromWorldMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "worldFromClipMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "clipTransform", 0, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH },
{ "viewFromWorldMatrix", 0, Type::MAT4, Precision::HIGH },
{ "worldFromViewMatrix", 0, Type::MAT4, Precision::HIGH },
{ "clipFromViewMatrix", 0, Type::MAT4, Precision::HIGH },
{ "viewFromClipMatrix", 0, Type::MAT4, Precision::HIGH },
{ "clipFromWorldMatrix", 0, Type::MAT4, Precision::HIGH },
{ "worldFromClipMatrix", 0, Type::MAT4, Precision::HIGH },
{ "clipTransform", 0, Type::FLOAT4, Precision::HIGH },
{ "clipControl", 0, UniformInterfaceBlock::Type::FLOAT2 },
{ "time", 0, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH },
{ "temporalNoise", 0, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH },
{ "userTime", 0, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH },
{ "clipControl", 0, Type::FLOAT2 },
{ "time", 0, Type::FLOAT, Precision::HIGH },
{ "temporalNoise", 0, Type::FLOAT, Precision::HIGH },
{ "userTime", 0, Type::FLOAT4, Precision::HIGH },
// ------------------------------------------------------------------------------------
// values below should only be accessed in surface materials
// ------------------------------------------------------------------------------------
{ "origin", 0, UniformInterfaceBlock::Type::FLOAT2, Precision::HIGH },
{ "offset", 0, UniformInterfaceBlock::Type::FLOAT2, Precision::HIGH },
{ "resolution", 0, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH },
{ "origin", 0, Type::FLOAT2, Precision::HIGH },
{ "offset", 0, Type::FLOAT2, Precision::HIGH },
{ "resolution", 0, Type::FLOAT4, Precision::HIGH },
{ "lodBias", 0, UniformInterfaceBlock::Type::FLOAT },
{ "refractionLodOffset", 0, UniformInterfaceBlock::Type::FLOAT },
{ "padding1", 0, UniformInterfaceBlock::Type::FLOAT },
{ "padding2", 0, UniformInterfaceBlock::Type::FLOAT },
{ "lodBias", 0, Type::FLOAT },
{ "refractionLodOffset", 0, Type::FLOAT },
{ "padding1", 0, Type::FLOAT },
{ "padding2", 0, Type::FLOAT },
{ "cameraPosition", 0, UniformInterfaceBlock::Type::FLOAT3, Precision::HIGH },
{ "oneOverFarMinusNear", 0, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH },
{ "worldOffset", 0, UniformInterfaceBlock::Type::FLOAT3 },
{ "nearOverFarMinusNear", 0, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH },
{ "cameraFar", 0, UniformInterfaceBlock::Type::FLOAT },
{ "exposure", 0, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH }, // high precision to work around #3602 (qualcom),
{ "ev100", 0, UniformInterfaceBlock::Type::FLOAT },
{ "needsAlphaChannel", 0, UniformInterfaceBlock::Type::FLOAT },
{ "cameraPosition", 0, Type::FLOAT3, Precision::HIGH },
{ "oneOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH },
{ "worldOffset", 0, Type::FLOAT3 },
{ "nearOverFarMinusNear", 0, Type::FLOAT, Precision::HIGH },
{ "cameraFar", 0, Type::FLOAT },
{ "exposure", 0, Type::FLOAT, Precision::HIGH }, // high precision to work around #3602 (qualcom),
{ "ev100", 0, Type::FLOAT },
{ "needsAlphaChannel", 0, Type::FLOAT },
// AO
{ "aoSamplingQualityAndEdgeDistance", 0, UniformInterfaceBlock::Type::FLOAT },
{ "aoBentNormals", 0, UniformInterfaceBlock::Type::FLOAT },
{ "aoReserved0", 0, UniformInterfaceBlock::Type::FLOAT },
{ "aoReserved1", 0, UniformInterfaceBlock::Type::FLOAT },
{ "aoSamplingQualityAndEdgeDistance", 0, Type::FLOAT },
{ "aoBentNormals", 0, Type::FLOAT },
{ "aoReserved0", 0, Type::FLOAT },
{ "aoReserved1", 0, Type::FLOAT },
// ------------------------------------------------------------------------------------
// Dynamic Lighting [variant: DYN]
// ------------------------------------------------------------------------------------
{ "zParams", 0, UniformInterfaceBlock::Type::FLOAT4 },
{ "fParams", 0, UniformInterfaceBlock::Type::UINT3 },
{ "lightChannels", 0, UniformInterfaceBlock::Type::UINT },
{ "froxelCountXY", 0, UniformInterfaceBlock::Type::FLOAT2 },
{ "zParams", 0, Type::FLOAT4 },
{ "fParams", 0, Type::UINT3 },
{ "lightChannels", 0, Type::UINT },
{ "froxelCountXY", 0, Type::FLOAT2 },
{ "iblLuminance", 0, UniformInterfaceBlock::Type::FLOAT },
{ "iblRoughnessOneLevel", 0, UniformInterfaceBlock::Type::FLOAT },
{ "iblSH", 9, UniformInterfaceBlock::Type::FLOAT3 },
{ "iblLuminance", 0, Type::FLOAT },
{ "iblRoughnessOneLevel", 0, Type::FLOAT },
{ "iblSH", 9, Type::FLOAT3 },
// ------------------------------------------------------------------------------------
// Directional Lighting [variant: DIR]
// ------------------------------------------------------------------------------------
{ "lightDirection", 0, UniformInterfaceBlock::Type::FLOAT3 },
{ "padding0", 0, UniformInterfaceBlock::Type::FLOAT },
{ "lightColorIntensity", 0, UniformInterfaceBlock::Type::FLOAT4 },
{ "sun", 0, UniformInterfaceBlock::Type::FLOAT4 },
{ "lightFarAttenuationParams", 0, UniformInterfaceBlock::Type::FLOAT2 },
{ "lightDirection", 0, Type::FLOAT3 },
{ "padding0", 0, Type::FLOAT },
{ "lightColorIntensity", 0, Type::FLOAT4 },
{ "sun", 0, Type::FLOAT4 },
{ "lightFarAttenuationParams", 0, Type::FLOAT2 },
// ------------------------------------------------------------------------------------
// Directional light shadowing [variant: SRE | DIR]
// ------------------------------------------------------------------------------------
{ "directionalShadows", 0, UniformInterfaceBlock::Type::UINT },
{ "ssContactShadowDistance",0, UniformInterfaceBlock::Type::FLOAT },
{ "directionalShadows", 0, Type::UINT },
{ "ssContactShadowDistance",0, Type::FLOAT },
{ "cascadeSplits", 0, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH },
{ "cascades", 0, UniformInterfaceBlock::Type::UINT },
{ "shadowBulbRadiusLs", 0, UniformInterfaceBlock::Type::FLOAT },
{ "shadowBias", 0, UniformInterfaceBlock::Type::FLOAT },
{ "shadowPenumbraRatioScale", 0, UniformInterfaceBlock::Type::FLOAT },
{ "lightFromWorldMatrix", 4, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "cascadeSplits", 0, Type::FLOAT4, Precision::HIGH },
{ "cascades", 0, Type::UINT },
{ "shadowBulbRadiusLs", 0, Type::FLOAT },
{ "shadowBias", 0, Type::FLOAT },
{ "shadowPenumbraRatioScale", 0, Type::FLOAT },
{ "lightFromWorldMatrix", 4, Type::MAT4, Precision::HIGH },
// ------------------------------------------------------------------------------------
// VSM shadows [variant: VSM]
// ------------------------------------------------------------------------------------
{ "vsmExponent", 0, UniformInterfaceBlock::Type::FLOAT },
{ "vsmDepthScale", 0, UniformInterfaceBlock::Type::FLOAT },
{ "vsmLightBleedReduction", 0, UniformInterfaceBlock::Type::FLOAT },
{ "shadowSamplingType", 0, UniformInterfaceBlock::Type::UINT },
{ "vsmExponent", 0, Type::FLOAT },
{ "vsmDepthScale", 0, Type::FLOAT },
{ "vsmLightBleedReduction", 0, Type::FLOAT },
{ "shadowSamplingType", 0, Type::UINT },
// ------------------------------------------------------------------------------------
// Fog [variant: FOG]
// ------------------------------------------------------------------------------------
{ "fogStart", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogMaxOpacity", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogHeight", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogHeightFalloff", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogColor", 0, UniformInterfaceBlock::Type::FLOAT3 },
{ "fogDensity", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogInscatteringStart", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogInscatteringSize", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogColorFromIbl", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogReserved0", 0, UniformInterfaceBlock::Type::FLOAT },
{ "fogStart", 0, Type::FLOAT },
{ "fogMaxOpacity", 0, Type::FLOAT },
{ "fogHeight", 0, Type::FLOAT },
{ "fogHeightFalloff", 0, Type::FLOAT },
{ "fogColor", 0, Type::FLOAT3 },
{ "fogDensity", 0, Type::FLOAT },
{ "fogInscatteringStart", 0, Type::FLOAT },
{ "fogInscatteringSize", 0, Type::FLOAT },
{ "fogColorFromIbl", 0, Type::FLOAT },
{ "fogReserved0", 0, Type::FLOAT },
// ------------------------------------------------------------------------------------
// Screen-space reflections [variant: SSR (i.e.: VSM | SRE)]
// ------------------------------------------------------------------------------------
{ "ssrReprojection", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "ssrUvFromViewMatrix", 0, UniformInterfaceBlock::Type::MAT4, Precision::HIGH },
{ "ssrThickness", 0, UniformInterfaceBlock::Type::FLOAT },
{ "ssrBias", 0, UniformInterfaceBlock::Type::FLOAT },
{ "ssrDistance", 0, UniformInterfaceBlock::Type::FLOAT },
{ "ssrStride", 0, UniformInterfaceBlock::Type::FLOAT },
{ "ssrReprojection", 0, Type::MAT4, Precision::HIGH },
{ "ssrUvFromViewMatrix", 0, Type::MAT4, Precision::HIGH },
{ "ssrThickness", 0, Type::FLOAT },
{ "ssrBias", 0, Type::FLOAT },
{ "ssrDistance", 0, Type::FLOAT },
{ "ssrStride", 0, Type::FLOAT },
// bring PerViewUib to 2 KiB
{ "reserved", sizeof(PerViewUib::reserved)/16, UniformInterfaceBlock::Type::FLOAT4 }
{ "reserved", sizeof(PerViewUib::reserved)/16, Type::FLOAT4 }
})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(PerRenderableUib::_name)
.add({{ "data", CONFIG_MAX_INSTANCES, UniformInterfaceBlock::Type::STRUCT, {},
.add({{ "data", CONFIG_MAX_INSTANCES, BufferInterfaceBlock::Type::STRUCT, {},
"PerRenderableData", sizeof(PerRenderableData), "CONFIG_MAX_INSTANCES" }})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getLightsUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getLightsUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(LightsUib::_name)
.add({{ "lights", CONFIG_MAX_LIGHT_COUNT,
UniformInterfaceBlock::Type::MAT4, Precision::HIGH }})
BufferInterfaceBlock::Type::MAT4, Precision::HIGH }})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(ShadowUib::_name)
.add({{ "shadows", CONFIG_MAX_SHADOW_CASTING_SPOTS,
UniformInterfaceBlock::Type::STRUCT, {},
BufferInterfaceBlock::Type::STRUCT, {},
"ShadowData", sizeof(ShadowUib::ShadowData) }})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getPerRenderableBonesUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getPerRenderableBonesUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(PerRenderableBoneUib::_name)
.add({{ "bones", CONFIG_MAX_BONE_COUNT,
UniformInterfaceBlock::Type::STRUCT, {},
"BoneData", sizeof(PerRenderableBoneUib::BoneData) }})
BufferInterfaceBlock::Type::STRUCT, {},
"BoneData", sizeof(PerRenderableBoneUib::BoneData) }})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getPerRenderableMorphingUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getPerRenderableMorphingUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(PerRenderableMorphingUib::_name)
.add({{ "weights", CONFIG_MAX_MORPH_TARGET_COUNT,
UniformInterfaceBlock::Type::FLOAT4 }})
BufferInterfaceBlock::Type::FLOAT4 }})
.build();
return uib;
}
UniformInterfaceBlock const& UibGenerator::getFroxelRecordUib() noexcept {
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
BufferInterfaceBlock const& UibGenerator::getFroxelRecordUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(FroxelRecordUib::_name)
.add({{ "records", 1024, UniformInterfaceBlock::Type::UINT4, Precision::HIGH }})
.add({{ "records", 1024, BufferInterfaceBlock::Type::UINT4, Precision::HIGH }})
.build();
return uib;
}

View File

@@ -19,17 +19,17 @@
namespace filament {
class UniformInterfaceBlock;
class BufferInterfaceBlock;
class UibGenerator {
public:
static UniformInterfaceBlock const& getPerViewUib() noexcept;
static UniformInterfaceBlock const& getPerRenderableUib() noexcept;
static UniformInterfaceBlock const& getLightsUib() noexcept;
static UniformInterfaceBlock const& getShadowUib() noexcept;
static UniformInterfaceBlock const& getPerRenderableBonesUib() noexcept;
static UniformInterfaceBlock const& getPerRenderableMorphingUib() noexcept;
static UniformInterfaceBlock const& getFroxelRecordUib() noexcept;
static BufferInterfaceBlock const& getPerViewUib() noexcept;
static BufferInterfaceBlock const& getPerRenderableUib() noexcept;
static BufferInterfaceBlock const& getLightsUib() noexcept;
static BufferInterfaceBlock const& getShadowUib() noexcept;
static BufferInterfaceBlock const& getPerRenderableBonesUib() noexcept;
static BufferInterfaceBlock const& getPerRenderableMorphingUib() noexcept;
static BufferInterfaceBlock const& getFroxelRecordUib() noexcept;
// When adding an UBO here, make sure to also update
// MaterialBuilder::writeCommonChunks() if needed
};

View File

@@ -19,7 +19,7 @@
#include "../SamplerBindingMap.h"
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <utility>
@@ -29,14 +29,14 @@ using namespace filament;
namespace filamat {
MaterialUniformInterfaceBlockChunk::MaterialUniformInterfaceBlockChunk(
UniformInterfaceBlock const& uib) :
BufferInterfaceBlock const& uib) :
Chunk(ChunkType::MaterialUib),
mUib(uib) {
}
void MaterialUniformInterfaceBlockChunk::flatten(Flattener& f) {
f.writeString(mUib.getName());
auto uibFields = mUib.getUniformInfoList();
auto uibFields = mUib.getFieldInfoList();
f.writeUint64(uibFields.size());
for (auto uInfo: uibFields) {
f.writeString(uInfo.name.c_str());

View File

@@ -26,7 +26,7 @@
namespace filament {
class SamplerBindingMap;
class SamplerInterfaceBlock;
class UniformInterfaceBlock;
class BufferInterfaceBlock;
struct SubpassInfo;
} // namespace filament
@@ -34,13 +34,13 @@ namespace filamat {
class MaterialUniformInterfaceBlockChunk final : public Chunk {
public:
explicit MaterialUniformInterfaceBlockChunk(filament::UniformInterfaceBlock const& uib);
explicit MaterialUniformInterfaceBlockChunk(filament::BufferInterfaceBlock const& uib);
~MaterialUniformInterfaceBlockChunk() final = default;
private:
void flatten(Flattener&) final;
filament::UniformInterfaceBlock const& mUib;
filament::BufferInterfaceBlock const& mUib;
};
// ------------------------------------------------------------------------------------------------

View File

@@ -54,14 +54,53 @@ static std::string_view getMaterialFunctionName(MaterialBuilder::MaterialDomain
return "material";
case MaterialBuilder::MaterialDomain::POST_PROCESS:
return "postProcess";
case MaterialBuilder::MaterialDomain::COMPUTE:
return "compute";
}
};
bool GLSLTools::analyzeComputeShader(const std::string& shaderCode,
filament::backend::ShaderModel model, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& info) noexcept {
// Parse to check syntax and semantic.
const char* shaderCString = shaderCode.c_str();
TShader tShader(EShLanguage::EShLangCompute);
tShader.setStrings(&shaderCString, 1);
GLSLangCleaner cleaner;
const int version = getGlslDefaultVersion(model);
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
utils::slog.e << "ERROR: Unable to parse compute shader:" << utils::io::endl;
utils::slog.e << tShader.getInfoLog() << utils::io::flush;
return false;
}
auto materialFunctionName = getMaterialFunctionName(filament::MaterialDomain::COMPUTE);
TIntermNode* root = tShader.getIntermediate()->getTreeRoot();
// Check there is a material function definition in this shader.
TIntermNode* materialFctNode = ASTUtils::getFunctionByNameOnly(materialFunctionName, *root);
if (materialFctNode == nullptr) {
utils::slog.e << "ERROR: Invalid compute shader:" << utils::io::endl;
utils::slog.e << "ERROR: Unable to find " << materialFunctionName << "() function" << utils::io::endl;
return false;
}
return true;
}
bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode,
filament::backend::ShaderModel model, MaterialBuilder::MaterialDomain materialDomain,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
bool hasCustomSurfaceShading, MaterialInfo const& info) noexcept {
assert_invariant(materialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
// Parse to check syntax and semantic.
const char* shaderCString = shaderCode.c_str();
@@ -69,7 +108,7 @@ bool GLSLTools::analyzeFragmentShader(const std::string& shaderCode,
tShader.setStrings(&shaderCString, 1);
GLSLangCleaner cleaner;
int version = glslangVersionFromShaderModel(model);
const int version = getGlslDefaultVersion(model);
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
@@ -131,6 +170,8 @@ bool GLSLTools::analyzeVertexShader(const std::string& shaderCode,
MaterialBuilder::MaterialDomain materialDomain, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& info) noexcept {
assert_invariant(materialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
// TODO: After implementing post-process vertex shaders, properly analyze them here.
if (materialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
return true;
@@ -143,7 +184,7 @@ bool GLSLTools::analyzeVertexShader(const std::string& shaderCode,
tShader.setStrings(&shaderCString, 1);
GLSLangCleaner cleaner;
int version = glslangVersionFromShaderModel(model);
const int version = getGlslDefaultVersion(model);
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
@@ -184,8 +225,9 @@ bool GLSLTools::findProperties(
auto getShaderStage = [](ShaderStage type) {
switch (type) {
case ShaderStage::VERTEX: return EShLanguage::EShLangVertex;
case ShaderStage::FRAGMENT: return EShLanguage::EShLangFragment;
case ShaderStage::VERTEX: return EShLanguage::EShLangVertex;
case ShaderStage::FRAGMENT: return EShLanguage::EShLangFragment;
case ShaderStage::COMPUTE: return EShLanguage::EShLangCompute;
}
};
@@ -193,7 +235,7 @@ bool GLSLTools::findProperties(
tShader.setStrings(&shaderCString, 1);
GLSLangCleaner cleaner;
int version = glslangVersionFromShaderModel(model);
const int version = getGlslDefaultVersion(model);
EShMessages msg = glslangFlagsFromTargetApi(targetApi, targetLanguage);
const TBuiltInResource* builtins = &DefaultTBuiltInResource;
bool ok = tShader.parse(builtins, version, false, msg);
@@ -321,17 +363,28 @@ bool GLSLTools::findSymbolsUsage(std::string_view functionSignature, TIntermNode
return true;
}
int GLSLTools::glslangVersionFromShaderModel(ShaderModel model) {
int version = 110;
// use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
// this is intended to be used with glslang's parse() method, which will figure out the actual
// version.
int GLSLTools::getGlslDefaultVersion(ShaderModel model) {
switch (model) {
case ShaderModel::MOBILE:
return 100;
case ShaderModel::DESKTOP:
return 110;
}
}
// The shading language version. Corresponds to #version $VALUE.
std::pair<int, bool> GLSLTools::getShadingLanguageVersion(ShaderModel model,
filament::backend::FeatureLevel featureLevel) {
using FeatureLevel = filament::backend::FeatureLevel;
switch (model) {
case ShaderModel::MOBILE:
version = 100;
break;
return { featureLevel >= FeatureLevel::FEATURE_LEVEL_2 ? 310 : 300, true };
case ShaderModel::DESKTOP:
version = 110;
break;
return { featureLevel >= FeatureLevel::FEATURE_LEVEL_2 ? 430 : 410, false };
}
return version;
}
EShMessages GLSLTools::glslangFlagsFromTargetApi(

View File

@@ -133,7 +133,12 @@ public:
MaterialBuilder::MaterialDomain materialDomain, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& info) noexcept;
// Public for unit tests.
static bool analyzeComputeShader(const std::string& shaderCode,
filament::backend::ShaderModel model, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& info) noexcept;
// Public for unit tests.
using Property = MaterialBuilder::Property;
using ShaderModel = filament::backend::ShaderModel;
// Use static code analysis on the fragment shader AST to guess properties used in user provided
@@ -146,7 +151,16 @@ public:
MaterialBuilder::TargetLanguage targetLanguage = MaterialBuilder::TargetLanguage::GLSL,
ShaderModel model = ShaderModel::DESKTOP) const noexcept;
static int glslangVersionFromShaderModel(filament::backend::ShaderModel model);
// use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
// this is intended to be used with glslang's parse() method, which will figure out the actual
// version.
static int getGlslDefaultVersion(filament::backend::ShaderModel model);
// The shading language version. Corresponds to #version $VALUE.
// returns the version and a boolean (true for essl, false for glsl)
static std::pair<int, bool> getShadingLanguageVersion(
filament::backend::ShaderModel model,
filament::backend::FeatureLevel featureLevel);
static EShMessages glslangFlagsFromTargetApi(MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage);

View File

@@ -39,7 +39,7 @@ io::sstream& CodeGenerator::generateSeparator(io::sstream& out) {
return out;
}
utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, ShaderStage type,
utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, ShaderStage stage,
MaterialInfo const& material) const {
switch (mShaderModel) {
case ShaderModel::MOBILE:
@@ -74,6 +74,13 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
// #included code. This way, glslang reports errors more accurately.
out << "#extension GL_GOOGLE_cpp_style_line_directive : enable\n\n";
if (stage == ShaderStage::COMPUTE) {
out << "layout(local_size_x = " << material.groupSize.x
<< ", local_size_y = " << material.groupSize.y
<< ", local_size_z = " << material.groupSize.z
<< ") in;\n\n";
}
if (mTargetApi == TargetApi::VULKAN) {
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
}
@@ -103,7 +110,7 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
out << "#define FILAMENT_HAS_FEATURE_TEXTURE_GATHER\n";
}
Precision defaultPrecision = getDefaultPrecision(type);
Precision defaultPrecision = getDefaultPrecision(stage);
const char* precision = getPrecisionQualifier(defaultPrecision, Precision::DEFAULT);
out << "precision " << precision << " float;\n";
out << "precision " << precision << " int;\n";
@@ -118,23 +125,33 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
generateSpecificationConstant(out, "CONFIG_MAX_INSTANCES", 1, (int)CONFIG_MAX_INSTANCES);
out << '\n';
out << SHADERS_COMMON_TYPES_GLSL_DATA;
out << SHADERS_COMMON_DEFINES_GLSL_DATA;
if (stage != ShaderStage::COMPUTE) {
out << '\n';
out << SHADERS_COMMON_TYPES_GLSL_DATA;
}
out << "\n";
return out;
}
Precision CodeGenerator::getDefaultPrecision(ShaderStage type) const {
if (type == ShaderStage::VERTEX) {
return Precision::HIGH;
} else if (type == ShaderStage::FRAGMENT) {
if (mShaderModel < ShaderModel::DESKTOP) {
return Precision::MEDIUM;
} else {
Precision CodeGenerator::getDefaultPrecision(ShaderStage stage) const {
switch (stage) {
case ShaderStage::VERTEX:
return Precision::HIGH;
}
break;
case ShaderStage::FRAGMENT:
if (mShaderModel < ShaderModel::DESKTOP) {
return Precision::MEDIUM;
} else {
return Precision::HIGH;
}
break;
case ShaderStage::COMPUTE:
return Precision::HIGH;
break;
}
return Precision::HIGH;
}
Precision CodeGenerator::getDefaultUniformPrecision() const {
@@ -150,11 +167,17 @@ io::sstream& CodeGenerator::generateEpilog(io::sstream& out) {
return out;
}
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderStage type) {
if (type == ShaderStage::VERTEX) {
out << SHADERS_MAIN_VS_DATA;
} else if (type == ShaderStage::FRAGMENT) {
out << SHADERS_MAIN_FS_DATA;
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX:
out << SHADERS_MAIN_VS_DATA;
break;
case ShaderStage::FRAGMENT:
out << SHADERS_MAIN_FS_DATA;
break;
case ShaderStage::COMPUTE:
out << SHADERS_MAIN_CS_DATA;
break;
}
return out;
}
@@ -315,39 +338,97 @@ const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precis
return getPrecisionQualifier(precision, defaultPrecision);
}
io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderStage type,
UniformBindingPoints binding, const UniformInterfaceBlock& uib) const {
auto const& infos = uib.getUniformInfoList();
utils::io::sstream& CodeGenerator::generateBuffers(utils::io::sstream& out,
MaterialInfo::BufferContainer const& buffers) const {
uint32_t binding = 0;
for (auto const* buffer : buffers) {
generateBufferInterfaceBlock(out, ShaderStage::COMPUTE, binding, *buffer);
binding++;
}
return out;
}
io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderStage stage,
UniformBindingPoints binding, const BufferInterfaceBlock& uib) const {
return generateBufferInterfaceBlock(out, stage, +binding, uib);
}
io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, ShaderStage stage,
uint32_t binding, const BufferInterfaceBlock& uib) const {
auto const& infos = uib.getFieldInfoList();
if (infos.empty()) {
return out;
}
const std::string_view blockName = uib.getName();
std::string instanceName(uib.getName());
std::string blockName{ uib.getName() };
std::string instanceName{ uib.getName() };
blockName.front() = char(std::toupper((unsigned char)blockName.front()));
instanceName.front() = char(std::tolower((unsigned char)instanceName.front()));
Precision uniformPrecision = getDefaultUniformPrecision();
Precision defaultPrecision = getDefaultPrecision(type);
Precision defaultPrecision = getDefaultPrecision(stage);
// This constant must match the equivalent in MetalState.h.
static constexpr uint32_t METAL_UNIFORM_BUFFER_BINDING_START = 17u;
out << "\nlayout(";
// TODO: at feature level 2, GLSL should support the binding qualifier
if (mTargetLanguage == TargetLanguage::SPIRV) {
const auto bindingIndex = (uint32_t) binding; // avoid char output
if (mTargetLanguage == TargetLanguage::SPIRV ||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
switch (mTargetApi) {
case TargetApi::METAL:
out << "binding = " << METAL_UNIFORM_BUFFER_BINDING_START + bindingIndex << ", ";
out << "binding = " << METAL_UNIFORM_BUFFER_BINDING_START + binding << ", ";
break;
default:
case TargetApi::OPENGL:
// GLSL 4.5 / ESSL 3.1 require the 'binding' layout qualifier
case TargetApi::VULKAN:
out << "binding = " << bindingIndex << ", ";
out << "binding = " << binding << ", ";
break;
case TargetApi::ALL:
// nonsensical, shouldn't happen.
break;
}
}
out << "std140) uniform " << blockName << " {\n";
switch (uib.getAlignment()) {
case BufferInterfaceBlock::Alignment::std140:
out << "std140";
break;
case BufferInterfaceBlock::Alignment::std430:
out << "std430";
break;
}
out << ") ";
switch (uib.getTarget()) {
case BufferInterfaceBlock::Target::UNIFORM:
out << "uniform ";
break;
case BufferInterfaceBlock::Target::SSBO:
out << "buffer ";
break;
}
out << blockName << " ";
if (uib.getTarget() == BufferInterfaceBlock::Target::SSBO) {
uint8_t qualifiers = uib.getQualifier();
while (qualifiers) {
uint8_t mask = 1u << utils::ctz(unsigned(qualifiers));
switch (BufferInterfaceBlock::Qualifier(qualifiers & mask)) {
case BufferInterfaceBlock::Qualifier::COHERENT: out << "coherent "; break;
case BufferInterfaceBlock::Qualifier::WRITEONLY: out << "writeonly "; break;
case BufferInterfaceBlock::Qualifier::READONLY: out << "readonly "; break;
case BufferInterfaceBlock::Qualifier::VOLATILE: out << "volatile "; break;
case BufferInterfaceBlock::Qualifier::RESTRICT: out << "restrict "; break;
}
qualifiers &= ~mask;
}
}
out << "{\n";
for (auto const& info : infos) {
char const* const type = getUniformTypeName(info);
char const* const precision = getUniformPrecisionQualifier(info.type, info.precision,
@@ -355,9 +436,13 @@ io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderStage type,
out << " " << precision;
if (precision[0] != '\0') out << " ";
out << type << " " << info.name.c_str();
if (info.size > 0) {
if (info.isArray) {
if (info.sizeName.empty()) {
out << "[" << info.size << "]";
if (info.size) {
out << "[" << info.size << "]";
} else {
out << "[]";
}
} else {
out << "[" << info.sizeName.c_str() << "]";
}
@@ -461,7 +546,7 @@ void CodeGenerator::fixupExternalSamplers(
// This method should only be called on shaders that have external samplers but since
// they may have been removed by previous optimization steps, we check again here
if (hasExternalSampler) {
// Find the #version line so we can insert the #extension directive
// Find the #version line, so we can insert the #extension directive
size_t index = shader.find("#version");
index += 8;
@@ -503,7 +588,7 @@ utils::io::sstream& CodeGenerator::generateSpecificationConstant(utils::io::sstr
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
std::visit([&](auto&& arg) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << " = " << arg << ";\n";
<< types[value.index()] << " " << name << " = " << arg << ";\n\n";
}, value);
} else {
std::visit([&](auto&& arg) {
@@ -511,7 +596,7 @@ utils::io::sstream& CodeGenerator::generateSpecificationConstant(utils::io::sstr
<< "#define SPIRV_CROSS_CONSTANT_ID_" << id << " " << arg << '\n'
<< "#endif" << '\n'
<< "const " << types[value.index()] << " " << name << " = SPIRV_CROSS_CONSTANT_ID_" << id
<< ";\n";
<< ";\n\n";
}, value);
}
return out;
@@ -556,14 +641,24 @@ io::sstream& CodeGenerator::generateQualityDefine(io::sstream& out, ShaderQualit
return out;
}
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderStage type) {
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderStage stage) {
out << SHADERS_COMMON_MATH_GLSL_DATA;
out << SHADERS_COMMON_SHADOWING_GLSL_DATA;
if (type == ShaderStage::VERTEX) {
} else if (type == ShaderStage::FRAGMENT) {
out << SHADERS_COMMON_SHADING_FS_DATA;
out << SHADERS_COMMON_GRAPHICS_FS_DATA;
out << SHADERS_COMMON_MATERIAL_FS_DATA;
switch (stage) {
case ShaderStage::VERTEX:
out << SHADERS_COMMON_SHADOWING_GLSL_DATA;
break;
case ShaderStage::FRAGMENT:
out << SHADERS_COMMON_SHADOWING_GLSL_DATA;
out << SHADERS_COMMON_SHADING_FS_DATA;
out << SHADERS_COMMON_GRAPHICS_FS_DATA;
out << SHADERS_COMMON_MATERIAL_FS_DATA;
break;
case ShaderStage::COMPUTE:
out << '\n';
// TODO: figure out if we need some common files here
break;
}
return out;
}
@@ -604,8 +699,7 @@ io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderSt
return out;
}
io::sstream& CodeGenerator::generatePostProcessGetters(io::sstream& out,
ShaderStage type) {
io::sstream& CodeGenerator::generatePostProcessGetters(io::sstream& out, ShaderStage type) {
out << SHADERS_COMMON_GETTERS_GLSL_DATA;
if (type == ShaderStage::VERTEX) {
out << SHADERS_POST_PROCESS_GETTERS_VS_DATA;
@@ -614,12 +708,18 @@ io::sstream& CodeGenerator::generatePostProcessGetters(io::sstream& out,
return out;
}
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderStage type) {
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderStage stage) {
out << SHADERS_COMMON_GETTERS_GLSL_DATA;
if (type == ShaderStage::VERTEX) {
out << SHADERS_GETTERS_VS_DATA;
} else if (type == ShaderStage::FRAGMENT) {
out << SHADERS_GETTERS_FS_DATA;
switch (stage) {
case ShaderStage::VERTEX:
out << SHADERS_GETTERS_VS_DATA;
break;
case ShaderStage::FRAGMENT:
out << SHADERS_GETTERS_FS_DATA;
break;
case ShaderStage::COMPUTE:
out << SHADERS_GETTERS_CS_DATA;
break;
}
return out;
}
@@ -739,8 +839,8 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
}
}
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::UniformInfo const& info) noexcept {
using Type = UniformInterfaceBlock::Type;
char const* CodeGenerator::getUniformTypeName(BufferInterfaceBlock::FieldInfo const& info) noexcept {
using Type = BufferInterfaceBlock::Type;
switch (info.type) {
case Type::BOOL: return "bool";
case Type::BOOL2: return "bvec2";
@@ -845,7 +945,7 @@ char const* CodeGenerator::getPrecisionQualifier(Precision precision,
}
/* static */
bool CodeGenerator::hasPrecision(UniformInterfaceBlock::Type type) noexcept {
bool CodeGenerator::hasPrecision(BufferInterfaceBlock::Type type) noexcept {
switch (type) {
case UniformType::BOOL:
case UniformType::BOOL2:

View File

@@ -21,6 +21,8 @@
#include <string>
#include <variant>
#include "MaterialInfo.h"
#include <utils/compiler.h>
#include <utils/Log.h>
@@ -29,7 +31,7 @@
#include <private/filament/EngineEnums.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
#include <backend/DriverEnums.h>
@@ -42,14 +44,21 @@
namespace filamat {
class UTILS_PRIVATE CodeGenerator {
using ShaderModel = filament::backend::ShaderModel;
using ShaderStage = filament::backend::ShaderStage;
using FeatureLevel = filament::backend::FeatureLevel;
using TargetApi = MaterialBuilder::TargetApi;
using TargetLanguage = MaterialBuilder::TargetLanguage;
using ShaderQuality = MaterialBuilder::ShaderQuality;
public:
CodeGenerator(filament::backend::ShaderModel shaderModel,
TargetApi targetApi, TargetLanguage targetLanguage) noexcept
: mShaderModel(shaderModel), mTargetApi(targetApi), mTargetLanguage(targetLanguage) {
CodeGenerator(ShaderModel shaderModel,
TargetApi targetApi,
TargetLanguage targetLanguage,
FeatureLevel featureLevel) noexcept
: mShaderModel(shaderModel),
mTargetApi(targetApi),
mTargetLanguage(targetLanguage),
mFeatureLevel(featureLevel) {
if (targetApi == TargetApi::ALL) {
utils::slog.e << "Must resolve target API before codegen." << utils::io::endl;
std::terminate();
@@ -62,20 +71,20 @@ public:
static utils::io::sstream& generateSeparator(utils::io::sstream& out);
// generate prolog for the given shader
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderStage type,
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderStage stage,
MaterialInfo const& material) const;
static utils::io::sstream& generateEpilog(utils::io::sstream& out);
// generate common functions for the given shader
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderStage stage);
static utils::io::sstream& generatePostProcessCommon(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateFog(utils::io::sstream& out, ShaderStage type);
// generate the shader's main()
static utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderStage stage);
static utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderStage type);
// generate the shader's code for the lit shading model
@@ -108,10 +117,6 @@ public:
// generate no-op shader for depth prepass
static utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderStage type);
// generate uniforms
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderStage type,
filament::UniformBindingPoints binding, const filament::UniformInterfaceBlock& uib) const;
// generate samplers
utils::io::sstream& generateSamplers(utils::io::sstream& out, uint8_t firstBinding,
const filament::SamplerInterfaceBlock& sib) const;
@@ -120,6 +125,18 @@ public:
static utils::io::sstream& generateSubpass(utils::io::sstream& out,
filament::SubpassInfo subpass);
// generate uniforms
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderStage stage,
filament::UniformBindingPoints binding, const filament::BufferInterfaceBlock& uib) const;
// generate buffers
utils::io::sstream& generateBuffers(utils::io::sstream& out,
MaterialInfo::BufferContainer const& buffers) const;
// generate an interface block
utils::io::sstream& generateBufferInterfaceBlock(utils::io::sstream& out, ShaderStage stage,
uint32_t binding, const filament::BufferInterfaceBlock& uib) const;
// generate material properties getters
static utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
MaterialBuilder::Property property, bool isSet);
@@ -136,14 +153,14 @@ public:
const char* name, uint32_t id, std::variant<int, float, bool> value) const;
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderStage type);
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderStage stage);
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderStage type);
static void fixupExternalSamplers(
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
private:
filament::backend::Precision getDefaultPrecision(ShaderStage type) const;
filament::backend::Precision getDefaultPrecision(ShaderStage stage) const;
filament::backend::Precision getDefaultUniformPrecision() const;
static const char* getUniformPrecisionQualifier(filament::backend::UniformType type,
@@ -161,12 +178,13 @@ private:
static char const* getPrecisionQualifier(filament::backend::Precision precision,
filament::backend::Precision defaultPrecision) noexcept;
filament::backend::ShaderModel mShaderModel;
ShaderModel mShaderModel;
TargetApi mTargetApi;
TargetLanguage mTargetLanguage;
FeatureLevel mFeatureLevel;
// return type name of uniform (e.g.: "vec3", "vec4", "float")
static char const* getUniformTypeName(filament::UniformInterfaceBlock::UniformInfo const& info) noexcept;
static char const* getUniformTypeName(filament::BufferInterfaceBlock::FieldInfo const& info) noexcept;
// return type name of output (e.g.: "vec3", "vec4", "float")
static char const* getOutputTypeName(MaterialBuilder::OutputType type) noexcept;
@@ -174,7 +192,7 @@ private:
// return qualifier for the specified interpolation mode
static char const* getInterpolationQualifier(filament::Interpolation interpolation) noexcept;
static bool hasPrecision(filament::UniformInterfaceBlock::Type type) noexcept;
static bool hasPrecision(filament::BufferInterfaceBlock::Type type) noexcept;
};
} // namespace filamat

View File

@@ -23,7 +23,7 @@
#include <filament/MaterialEnums.h>
#include <private/filament/UniformInterfaceBlock.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/SamplerInterfaceBlock.h>
#include <private/filament/SubpassInfo.h>
@@ -59,12 +59,16 @@ struct UTILS_PUBLIC MaterialInfo {
filament::BlendingMode blendingMode;
filament::BlendingMode postLightingBlendingMode;
filament::Shading shading;
filament::UniformInterfaceBlock uib;
filament::BufferInterfaceBlock uib;
filament::SamplerInterfaceBlock sib;
filament::SubpassInfo subpass;
filament::SamplerBindingMap samplerBindings;
filament::ShaderQuality quality;
filament::backend::FeatureLevel featureLevel;
filament::math::uint3 groupSize;
using BufferContainer = utils::FixedCapacityVector<filament::BufferInterfaceBlock const*>;
BufferContainer buffers{ BufferContainer::with_capacity(filament::backend::MAX_SSBO_COUNT) };
};
}

View File

@@ -135,6 +135,11 @@ ShaderGenerator::ShaderGenerator(
CString const& materialVertexCode, size_t vertexLineOffset,
MaterialBuilder::MaterialDomain materialDomain) noexcept {
if (materialDomain == MaterialBuilder::MaterialDomain::COMPUTE) {
// we shouldn't have a vertex shader in a compute material
assert_invariant(materialVertexCode.empty());
}
std::copy(std::begin(properties), std::end(properties), std::begin(mProperties));
std::copy(std::begin(variables), std::end(variables), std::begin(mVariables));
std::copy(std::begin(outputs), std::end(outputs), std::back_inserter(mOutputs));
@@ -154,6 +159,9 @@ ShaderGenerator::ShaderGenerator(
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
mMaterialFragmentCode =
CString("void postProcess(inout PostProcessInputs p) {\n}\n");
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::COMPUTE) {
mMaterialFragmentCode =
CString("void compute() {\n}\n");
}
}
if (mMaterialVertexCode.empty()) {
@@ -167,10 +175,22 @@ ShaderGenerator::ShaderGenerator(
}
}
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm,
std::string& shader, MaterialInfo const& material) noexcept {
// External samplers are only supported on GL ES at the moment, we must
// skip the fixup on desktop targets
if (material.hasExternalSamplers && sm == ShaderModel::MOBILE) {
CodeGenerator::fixupExternalSamplers(shader, material.sib);
}
}
std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, const filament::Variant variant, Interpolation interpolation,
VertexDomain vertexDomain) const noexcept {
assert_invariant(mMaterialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
return createPostProcessVertexProgram(shaderModel, targetApi,
targetLanguage, material, variant.key);
@@ -178,7 +198,7 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
io::sstream vs;
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, material.featureLevel);
const bool lit = material.isLit;
cg.generateProlog(vs, ShaderStage::VERTEX, material);
@@ -306,12 +326,15 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, const filament::Variant variant,
Interpolation interpolation) const noexcept {
assert_invariant(mMaterialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
return createPostProcessFragmentProgram(shaderModel, targetApi, targetLanguage, material,
variant.key);
}
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, material.featureLevel);
const bool lit = material.isLit;
io::sstream fs;
@@ -521,19 +544,49 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
return fs.c_str();
}
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm,
std::string& shader, MaterialInfo const& material) noexcept {
// External samplers are only supported on GL ES at the moment, we must
// skip the fixup on desktop targets
if (material.hasExternalSamplers && sm == ShaderModel::MOBILE) {
CodeGenerator::fixupExternalSamplers(shader, material.sib);
}
std::string ShaderGenerator::createComputeProgram(filament::backend::ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, filament::Variant variant) const noexcept {
assert_invariant(mMaterialDomain == MaterialBuilder::MaterialDomain::COMPUTE);
assert_invariant(material.featureLevel >= FeatureLevel::FEATURE_LEVEL_2);
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, material.featureLevel);
io::sstream s;
cg.generateProlog(s, ShaderStage::COMPUTE, material);
cg.generateQualityDefine(s, material.quality);
cg.generateUniforms(s, ShaderStage::COMPUTE,
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
cg.generateUniforms(s, ShaderStage::COMPUTE,
UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib);
cg.generateSamplers(s,
material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE),
material.sib);
// generate SSBO
cg.generateBuffers(s, material.buffers);
// TODO: generate images
CodeGenerator::generateCommon(s, ShaderStage::COMPUTE);
CodeGenerator::generateGetters(s, ShaderStage::COMPUTE);
appendShader(s, mMaterialFragmentCode, mMaterialLineOffset);
CodeGenerator::generateShaderMain(s, ShaderStage::COMPUTE);
CodeGenerator::generateEpilog(s);
return s.c_str();
}
std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, const filament::Variant::type_t variantKey) const noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
const CodeGenerator cg(sm, targetApi, targetLanguage, material.featureLevel);
io::sstream vs;
cg.generateProlog(vs, ShaderStage::VERTEX, material);
@@ -573,7 +626,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel sm,
std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variant) const noexcept {
const CodeGenerator cg(sm, targetApi, targetLanguage);
const CodeGenerator cg(sm, targetApi, targetLanguage, material.featureLevel);
io::sstream fs;
cg.generateProlog(fs, ShaderStage::FRAGMENT, material);
@@ -622,5 +675,4 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
return fs.c_str();
}
} // namespace filament

View File

@@ -59,6 +59,10 @@ public:
MaterialInfo const& material, filament::Variant variant,
filament::Interpolation interpolation) const noexcept;
std::string createComputeProgram(filament::backend::ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, filament::Variant variant) const noexcept;
/**
* When a GLSL shader is optimized we run it through an intermediate SPIR-V
* representation. Unfortunately external samplers cannot be used with SPIR-V
@@ -84,7 +88,7 @@ private:
MaterialBuilder::OutputList mOutputs;
MaterialBuilder::MaterialDomain mMaterialDomain;
MaterialBuilder::PreprocessorDefineList mDefines;
utils::CString mMaterialFragmentCode;
utils::CString mMaterialFragmentCode; // fragment or compute code
utils::CString mMaterialVertexCode;
size_t mMaterialLineOffset;
size_t mMaterialVertexLineOffset;

View File

@@ -756,8 +756,8 @@ TEST_F(MaterialCompiler, Uv0AndUv1) {
TEST_F(MaterialCompiler, Arrays) {
filamat::MaterialBuilder builder;
builder.parameter(UniformType::FLOAT4, 1, "f4");
builder.parameter(UniformType::FLOAT, 1, "f1");
builder.parameter("f4", 1, UniformType::FLOAT4);
builder.parameter("f1", 1, UniformType::FLOAT);
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());

View File

@@ -158,22 +158,23 @@ std::string shaderFromConfig(MaterialConfig config) {
Material* createMaterialFromConfig(Engine& engine, MaterialConfig config ) {
std::string shader = shaderFromConfig(config);
MaterialBuilder::init();
MaterialBuilder builder = MaterialBuilder()
MaterialBuilder builder;
builder
.name("material")
.material(shader.c_str())
.doubleSided(config.doubleSided)
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "baseColorMap")
.parameter(MaterialBuilder::UniformType::FLOAT4, "baseColorFactor")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "metallicRoughnessMap")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "aoMap")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "emissiveMap")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "normalMap")
.parameter(MaterialBuilder::UniformType::FLOAT, "metallicFactor")
.parameter(MaterialBuilder::UniformType::FLOAT, "roughnessFactor")
.parameter(MaterialBuilder::UniformType::FLOAT, "normalScale")
.parameter(MaterialBuilder::UniformType::FLOAT, "aoStrength")
.parameter(MaterialBuilder::UniformType::FLOAT3, "emissiveFactor");
.parameter("baseColorMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("baseColorFactor", MaterialBuilder::UniformType::FLOAT4)
.parameter("metallicRoughnessMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("aoMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("emissiveMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("normalMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("metallicFactor", MaterialBuilder::UniformType::FLOAT)
.parameter("roughnessFactor", MaterialBuilder::UniformType::FLOAT)
.parameter("normalScale", MaterialBuilder::UniformType::FLOAT)
.parameter("aoStrength", MaterialBuilder::UniformType::FLOAT)
.parameter("emissiveFactor", MaterialBuilder::UniformType::FLOAT3);
if (config.maxUVIndex() > 0) {
builder.require(VertexAttribute::UV1);

View File

@@ -314,23 +314,23 @@ std::string shaderFromKey(const MaterialKey& config) {
return shader;
}
static Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& uvmap,
Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap& uvmap,
const char* name, bool optimizeShaders) {
std::string shader = shaderFromKey(config);
processShaderString(&shader, uvmap, config);
MaterialBuilder builder = MaterialBuilder()
.name(name)
.flipUV(false)
.specularAmbientOcclusion(MaterialBuilder::SpecularAmbientOcclusion::SIMPLE)
.specularAntiAliasing(true)
.clearCoatIorChange(false)
.material(shader.c_str())
.doubleSided(config.doubleSided)
.transparencyMode(config.doubleSided ?
MaterialBuilder::TransparencyMode::TWO_PASSES_TWO_SIDES :
MaterialBuilder::TransparencyMode::DEFAULT)
.reflectionMode(MaterialBuilder::ReflectionMode::SCREEN_SPACE)
.targetApi(filamat::targetApiFromBackend(engine->getBackend()));
MaterialBuilder builder;
builder.name(name)
.flipUV(false)
.specularAmbientOcclusion(MaterialBuilder::SpecularAmbientOcclusion::SIMPLE)
.specularAntiAliasing(true)
.clearCoatIorChange(false)
.material(shader.c_str())
.doubleSided(config.doubleSided)
.transparencyMode(config.doubleSided ?
MaterialBuilder::TransparencyMode::TWO_PASSES_TWO_SIDES :
MaterialBuilder::TransparencyMode::DEFAULT)
.reflectionMode(MaterialBuilder::ReflectionMode::SCREEN_SPACE)
.targetApi(filamat::targetApiFromBackend(engine->getBackend()));
if (!optimizeShaders) {
builder.optimization(MaterialBuilder::Optimization::NONE);
@@ -347,16 +347,16 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
}
if (config.enableDiagnostics) {
builder.parameter(MaterialBuilder::UniformType::BOOL, "enableDiagnostics");
builder.parameter("enableDiagnostics", MaterialBuilder::UniformType::BOOL);
}
// BASE COLOR
builder.parameter(MaterialBuilder::UniformType::FLOAT4, "baseColorFactor");
builder.parameter("baseColorFactor", MaterialBuilder::UniformType::FLOAT4);
if (config.hasBaseColorTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "baseColorMap");
builder.parameter("baseColorMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "baseColorUvMatrix");
builder.parameter("baseColorUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
if (config.hasVertexColors) {
@@ -364,99 +364,99 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
}
// METALLIC-ROUGHNESS
builder.parameter(MaterialBuilder::UniformType::FLOAT, "metallicFactor");
builder.parameter(MaterialBuilder::UniformType::FLOAT, "roughnessFactor");
builder.parameter("metallicFactor", MaterialBuilder::UniformType::FLOAT);
builder.parameter("roughnessFactor", MaterialBuilder::UniformType::FLOAT);
if (config.hasMetallicRoughnessTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "metallicRoughnessMap");
builder.parameter("metallicRoughnessMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "metallicRoughnessUvMatrix");
builder.parameter("metallicRoughnessUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
// SPECULAR-GLOSSINESS
if (config.useSpecularGlossiness) {
builder.parameter(MaterialBuilder::UniformType::FLOAT, "glossinessFactor");
builder.parameter(MaterialBuilder::UniformType::FLOAT3, "specularFactor");
builder.parameter("glossinessFactor", MaterialBuilder::UniformType::FLOAT);
builder.parameter("specularFactor", MaterialBuilder::UniformType::FLOAT3);
}
// NORMAL MAP
// In the glTF spec normalScale is in normalTextureInfo; in cgltf it is part of texture_view.
builder.parameter(MaterialBuilder::UniformType::FLOAT, "normalScale");
builder.parameter("normalScale", MaterialBuilder::UniformType::FLOAT);
if (config.hasNormalTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "normalMap");
builder.parameter("normalMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "normalUvMatrix");
builder.parameter("normalUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
// AMBIENT OCCLUSION
// In the glTF spec aoStrength is in occlusionTextureInfo; in cgltf it is part of texture_view.
builder.parameter(MaterialBuilder::UniformType::FLOAT, "aoStrength");
builder.parameter("aoStrength", MaterialBuilder::UniformType::FLOAT);
if (config.hasOcclusionTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "occlusionMap");
builder.parameter("occlusionMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "occlusionUvMatrix");
builder.parameter("occlusionUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
// EMISSIVE
builder.parameter(MaterialBuilder::UniformType::FLOAT3, "emissiveFactor");
builder.parameter(MaterialBuilder::UniformType::FLOAT, "emissiveStrength");
builder.parameter("emissiveFactor", MaterialBuilder::UniformType::FLOAT3);
builder.parameter("emissiveStrength", MaterialBuilder::UniformType::FLOAT);
if (config.hasEmissiveTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "emissiveMap");
builder.parameter("emissiveMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "emissiveUvMatrix");
builder.parameter("emissiveUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
// CLEAR COAT
if (config.hasClearCoat) {
builder.parameter(MaterialBuilder::UniformType::FLOAT, "clearCoatFactor");
builder.parameter(MaterialBuilder::UniformType::FLOAT, "clearCoatRoughnessFactor");
builder.parameter("clearCoatFactor", MaterialBuilder::UniformType::FLOAT);
builder.parameter("clearCoatRoughnessFactor", MaterialBuilder::UniformType::FLOAT);
if (config.hasClearCoatTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "clearCoatMap");
builder.parameter("clearCoatMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "clearCoatUvMatrix");
builder.parameter("clearCoatUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
if (config.hasClearCoatRoughnessTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "clearCoatRoughnessMap");
builder.parameter("clearCoatRoughnessMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "clearCoatRoughnessUvMatrix");
builder.parameter("clearCoatRoughnessUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
if (config.hasClearCoatNormalTexture) {
builder.parameter(MaterialBuilder::UniformType::FLOAT, "clearCoatNormalScale");
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "clearCoatNormalMap");
builder.parameter("clearCoatNormalScale", MaterialBuilder::UniformType::FLOAT);
builder.parameter("clearCoatNormalMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "clearCoatNormalUvMatrix");
builder.parameter("clearCoatNormalUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
}
// SHEEN
if (config.hasSheen) {
builder.parameter(MaterialBuilder::UniformType::FLOAT3, "sheenColorFactor");
builder.parameter(MaterialBuilder::UniformType::FLOAT, "sheenRoughnessFactor");
builder.parameter("sheenColorFactor", MaterialBuilder::UniformType::FLOAT3);
builder.parameter("sheenRoughnessFactor", MaterialBuilder::UniformType::FLOAT);
if (config.hasSheenColorTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "sheenColorMap");
builder.parameter("sheenColorMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "sheenColorUvMatrix");
builder.parameter("sheenColorUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
if (config.hasSheenRoughnessTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "sheenRoughnessMap");
builder.parameter("sheenRoughnessMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "sheenRoughnessUvMatrix");
builder.parameter("sheenRoughnessUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
}
@@ -472,12 +472,12 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
// window pane, but a poor job of rendering a glass full of liquid.
builder.refractionType(RefractionType::THIN);
builder.parameter(MaterialBuilder::UniformType::FLOAT, "transmissionFactor");
builder.parameter("transmissionFactor", MaterialBuilder::UniformType::FLOAT);
if (config.hasTransmissionTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "transmissionMap");
builder.parameter("transmissionMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "transmissionUvMatrix");
builder.parameter("transmissionUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
@@ -507,14 +507,14 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
// Override thin transmission if both extensions are used
builder.refractionType(RefractionType::SOLID);
builder.parameter(MaterialBuilder::UniformType::FLOAT3, "volumeAbsorption");
builder.parameter(MaterialBuilder::UniformType::FLOAT, "volumeThicknessFactor");
builder.parameter("volumeAbsorption", MaterialBuilder::UniformType::FLOAT3);
builder.parameter("volumeThicknessFactor", MaterialBuilder::UniformType::FLOAT);
if (config.hasVolumeThicknessTexture) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "volumeThicknessMap");
builder.parameter("volumeThicknessMap", MaterialBuilder::SamplerType::SAMPLER_2D);
if (config.hasTextureTransforms) {
builder.parameter(MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH, "volumeThicknessUvMatrix");
builder.parameter("volumeThicknessUvMatrix", MaterialBuilder::UniformType::MAT3,
MaterialBuilder::ParameterPrecision::HIGH);
}
}
@@ -523,7 +523,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const
// IOR
if (config.hasIOR) {
builder.parameter(MaterialBuilder::UniformType::FLOAT, "ior");
builder.parameter("ior", MaterialBuilder::UniformType::FLOAT);
}
if (config.unlit) {

View File

@@ -34,10 +34,10 @@ struct ShaderInfo {
uint32_t offset;
};
size_t getShaderCount(filaflat::ChunkContainer container, filamat::ChunkType type);
bool getMetalShaderInfo(filaflat::ChunkContainer container, ShaderInfo* info);
bool getGlShaderInfo(filaflat::ChunkContainer container, ShaderInfo* info);
bool getVkShaderInfo(filaflat::ChunkContainer container, ShaderInfo* info);
size_t getShaderCount(const filaflat::ChunkContainer& container, filamat::ChunkType type);
bool getMetalShaderInfo(const filaflat::ChunkContainer& container, ShaderInfo* info);
bool getGlShaderInfo(const filaflat::ChunkContainer& container, ShaderInfo* info);
bool getVkShaderInfo(const filaflat::ChunkContainer& container, ShaderInfo* info);
} // namespace matdbg
} // namespace filament

View File

@@ -22,6 +22,11 @@
namespace filament::matdbg {
std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept {
if (domain == MaterialDomain::COMPUTE) {
// Currently we have no variants for compute materials
return "---";
}
if (domain == MaterialDomain::POST_PROCESS) {
switch (PostProcessVariant{variant.key}) {
case PostProcessVariant::OPAQUE: return "OPA";

View File

@@ -38,8 +38,8 @@ static bool read(const filaflat::ChunkContainer& container, filamat::ChunkType t
if (!container.hasChunk(type)) {
return false;
}
filaflat::Unflattener unflattener(container.getChunkStart(type), container.getChunkEnd(type));
auto [start, end] = container.getChunkRange(type);
filaflat::Unflattener unflattener(start, end);
return unflattener.read(value);
}
@@ -95,6 +95,7 @@ const char* toString(MaterialDomain domain) noexcept {
switch (domain) {
case MaterialDomain::SURFACE: return "surface";
case MaterialDomain::POST_PROCESS: return "post process";
case MaterialDomain::COMPUTE: return "compute";
}
return "--";
}

View File

@@ -26,22 +26,20 @@
#include <backend/DriverEnums.h>
namespace filament {
namespace matdbg {
namespace filament::matdbg {
using namespace filament;
using namespace backend;
using namespace filaflat;
using namespace filamat;
using namespace std;
using namespace utils;
size_t getShaderCount(ChunkContainer container, filamat::ChunkType type) {
size_t getShaderCount(const ChunkContainer& container, ChunkType type) {
if (!container.hasChunk(type)) {
return 0;
}
Unflattener unflattener(container.getChunkStart(type), container.getChunkEnd(type));
auto [start, end] = container.getChunkRange(type);
Unflattener unflattener(start, end);
uint64_t shaderCount = 0;
if (!unflattener.read(&shaderCount) || shaderCount == 0) {
@@ -50,14 +48,13 @@ size_t getShaderCount(ChunkContainer container, filamat::ChunkType type) {
return shaderCount;
}
bool getMetalShaderInfo(ChunkContainer container, ShaderInfo* info) {
if (!container.hasChunk(filamat::ChunkType::MaterialMetal)) {
bool getMetalShaderInfo(const ChunkContainer& container, ShaderInfo* info) {
if (!container.hasChunk(ChunkType::MaterialMetal)) {
return true;
}
Unflattener unflattener(
container.getChunkStart(filamat::ChunkType::MaterialMetal),
container.getChunkEnd(filamat::ChunkType::MaterialMetal));
auto [start, end] = container.getChunkRange(ChunkType::MaterialMetal);
Unflattener unflattener(start, end);
uint64_t shaderCount = 0;
if (!unflattener.read(&shaderCount) || shaderCount == 0) {
@@ -97,14 +94,13 @@ bool getMetalShaderInfo(ChunkContainer container, ShaderInfo* info) {
return true;
}
bool getGlShaderInfo(ChunkContainer container, ShaderInfo* info) {
if (!container.hasChunk(filamat::ChunkType::MaterialGlsl)) {
bool getGlShaderInfo(const ChunkContainer& container, ShaderInfo* info) {
if (!container.hasChunk(ChunkType::MaterialGlsl)) {
return true;
}
Unflattener unflattener(
container.getChunkStart(filamat::ChunkType::MaterialGlsl),
container.getChunkEnd(filamat::ChunkType::MaterialGlsl));
auto [start, end] = container.getChunkRange(ChunkType::MaterialGlsl);
Unflattener unflattener(start, end);
uint64_t shaderCount;
if (!unflattener.read(&shaderCount) || shaderCount == 0) {
@@ -143,14 +139,13 @@ bool getGlShaderInfo(ChunkContainer container, ShaderInfo* info) {
return true;
}
bool getVkShaderInfo(ChunkContainer container, ShaderInfo* info) {
if (!container.hasChunk(filamat::ChunkType::MaterialSpirv)) {
bool getVkShaderInfo(const ChunkContainer& container, ShaderInfo* info) {
if (!container.hasChunk(ChunkType::MaterialSpirv)) {
return true;
}
Unflattener unflattener(
container.getChunkStart(filamat::ChunkType::MaterialSpirv),
container.getChunkEnd(filamat::ChunkType::MaterialSpirv));
auto [start, end] = container.getChunkRange(ChunkType::MaterialSpirv);
Unflattener unflattener(start, end);
uint64_t shaderCount;
if (!unflattener.read(&shaderCount) || shaderCount == 0) {
@@ -189,5 +184,4 @@ bool getVkShaderInfo(ChunkContainer container, ShaderInfo* info) {
return true;
}
} // namespace matdbg
} // namespace filament

View File

@@ -176,8 +176,9 @@ bool ShaderReplacer::replaceSpirv(ShaderModel shaderModel, Variant variant,
auto getShaderStage = [](ShaderStage type) {
switch (type) {
case ShaderStage::VERTEX: return EShLanguage::EShLangVertex;
case ShaderStage::FRAGMENT: return EShLanguage::EShLangFragment;
case ShaderStage::VERTEX: return EShLanguage::EShLangVertex;
case ShaderStage::FRAGMENT: return EShLanguage::EShLangFragment;
case ShaderStage::COMPUTE: return EShLanguage::EShLangCompute;
}
};
@@ -192,10 +193,10 @@ bool ShaderReplacer::replaceSpirv(ShaderModel shaderModel, Variant variant,
MaterialBuilder::TargetApi targetApi = targetApiFromBackend(mBackend);
assert_invariant(targetApi == MaterialBuilder::TargetApi::VULKAN);
const int langVersion = GLSLTools::glslangVersionFromShaderModel(shaderModel);
const int version = GLSLTools::getGlslDefaultVersion(shaderModel);
const EShMessages msg = GLSLTools::glslangFlagsFromTargetApi(targetApi,
MaterialBuilder::TargetLanguage::SPIRV);
const bool ok = tShader.parse(&DefaultTBuiltInResource, langVersion, false, msg);
const bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
if (!ok) {
slog.e << "ShaderReplacer parse:\n" << tShader.getInfoLog() << io::endl;
return false;

View File

@@ -159,9 +159,8 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
return true;
}
Unflattener uib(
container.getChunkStart(ChunkType::MaterialUib),
container.getChunkEnd(ChunkType::MaterialUib));
auto [startUib, endUib] = container.getChunkRange(ChunkType::MaterialUib);
Unflattener uib(startUib, endUib);
CString name;
if (!uib.read(&name)) {
@@ -173,9 +172,8 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
return false;
}
Unflattener sib(
container.getChunkStart(ChunkType::MaterialSib),
container.getChunkEnd(ChunkType::MaterialSib));
auto [startSib, endSib] = container.getChunkRange(ChunkType::MaterialSib);
Unflattener sib(startSib, endSib);
if (!sib.read(&name)) {
return false;
@@ -256,11 +254,18 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
<< endl;
}
text << endl;
return true;
}
static bool printSubpassesInfo(ostream& text, const ChunkContainer& container) {
// Subpasses are optional.
if (container.hasChunk(ChunkType::MaterialSubpass)) {
Unflattener subpasses(
container.getChunkStart(ChunkType::MaterialSubpass),
container.getChunkEnd(ChunkType::MaterialSubpass));
text << "Sub-passes:" << endl;
auto [start, end] = container.getChunkRange(ChunkType::MaterialSubpass);
Unflattener subpasses(start, end);
CString name;
if (!subpasses.read(&name)) {
@@ -308,10 +313,8 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
<< toString(SamplerFormat(fieldFormat))
<< endl;
}
text << endl;
}
text << endl;
return true;
}
@@ -403,6 +406,9 @@ bool TextWriter::writeMaterialInfo(const filaflat::ChunkContainer& container) {
if (!printParametersInfo(text, container)) {
return false;
}
if (!printSubpassesInfo(text, container)) {
return false;
}
if (!printGlslInfo(text, container)) {
return false;
}

View File

@@ -200,16 +200,17 @@ static void setup(Engine* engine, View* view, Scene* scene) {
}
MaterialBuilder::init();
MaterialBuilder builder = MaterialBuilder()
MaterialBuilder builder;
builder
.name("DefaultMaterial")
.targetApi(MaterialBuilder::TargetApi::ALL)
#ifndef NDEBUG
.optimization(MaterialBuilderBase::Optimization::NONE)
#endif
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "normalMap")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "basecolorMap")
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "roughnessMap")
.parameter("normalMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("basecolorMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.parameter("roughnessMap", MaterialBuilder::SamplerType::SAMPLER_2D)
.material(R"SHADER(
void material(inout MaterialInputs material) {
vec2 uv = getUV0() * 2.0;

View File

@@ -381,7 +381,8 @@ static void setup(Engine* engine, View* view, Scene* scene) {
shader += "}\n";
MaterialBuilder::init();
MaterialBuilder builder = MaterialBuilder()
MaterialBuilder builder;
builder
.name("DefaultMaterial")
.targetApi(MaterialBuilder::TargetApi::ALL)
#ifndef NDEBUG
@@ -399,7 +400,7 @@ static void setup(Engine* engine, View* view, Scene* scene) {
for (auto& map: g_maps) {
if (map.texture != nullptr) {
builder.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, map.parameterName);
builder.parameter(map.parameterName, MaterialBuilder::SamplerType::SAMPLER_2D);
}
}

View File

@@ -289,7 +289,8 @@ static void setup(Engine* engine, View*, Scene* scene) {
shader += "}\n";
MaterialBuilder::init();
MaterialBuilder builder = MaterialBuilder()
MaterialBuilder builder;
builder
.name("DefaultMaterial")
.targetApi(MaterialBuilder::TargetApi::ALL)
#ifndef NDEBUG
@@ -301,20 +302,20 @@ static void setup(Engine* engine, View*, Scene* scene) {
if (hasNormalMap) {
builder
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "normalMap");
.require(VertexAttribute::UV0)
.parameter("normalMap", MaterialBuilder::SamplerType::SAMPLER_2D);
}
if (hasClearCoatNormalMap) {
builder
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "clearCoatNormalMap");
.require(VertexAttribute::UV0)
.parameter("clearCoatNormalMap", MaterialBuilder::SamplerType::SAMPLER_2D);
}
if (hasBaseColorMap) {
builder
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "baseColorMap");
.require(VertexAttribute::UV0)
.parameter("baseColorMap", MaterialBuilder::SamplerType::SAMPLER_2D);
}
Package pkg = builder.build(engine->getJobSystem());

View File

@@ -14,6 +14,7 @@ set(SHADERS
src/ambient_occlusion.fs
src/attributes.vs
src/brdf.fs
src/common_defines.glsl
src/common_getters.glsl
src/common_graphics.fs
src/common_lighting.fs
@@ -24,12 +25,14 @@ set(SHADERS
src/common_types.glsl
src/depth_main.fs
src/fog.fs
src/getters.cs
src/getters.fs
src/getters.vs
src/light_directional.fs
src/light_indirect.fs
src/light_reflections.fs
src/light_punctual.fs
src/main.cs
src/main.fs
src/main.vs
src/material_inputs.fs

View File

@@ -0,0 +1,24 @@
#if defined(FILAMENT_VULKAN_SEMANTICS)
#define LAYOUT_LOCATION(x) layout(location = x)
#else
#define LAYOUT_LOCATION(x)
#endif
#define bool2 bvec2
#define bool3 bvec3
#define bool4 bvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define float3x3 mat3
#define float4x4 mat4

View File

@@ -1,28 +1,3 @@
#if defined(FILAMENT_VULKAN_SEMANTICS)
#define LAYOUT_LOCATION(x) layout(location = x)
#else
#define LAYOUT_LOCATION(x)
#endif
#define bool2 bvec2
#define bool3 bvec3
#define bool4 bvec4
#define int2 ivec2
#define int3 ivec3
#define int4 ivec4
#define uint2 uvec2
#define uint3 uvec3
#define uint4 uvec4
#define float2 vec2
#define float3 vec3
#define float4 vec4
#define float3x3 mat3
#define float4x4 mat4
// Adreno drivers seem to ignore precision qualifiers in structs, unless they're used in
// UBOs, which is is the case here.
struct ShadowData {

45
shaders/src/getters.cs Normal file
View File

@@ -0,0 +1,45 @@
//------------------------------------------------------------------------------
// Compute builtin access
//------------------------------------------------------------------------------
/**
* @public-api
* Work group the thread belongs to.
*/
uvec3 getWorkGroupID() {
return gl_WorkGroupID;
}
/**
* @public-api
* Number of work groups in each dimensions
*/
uvec3 getWorkGroupCount() {
return gl_NumWorkGroups;
}
/**
* @public-api
* Within the work group, get a unique identifier for the thread.
*/
uvec3 getLocalInvocationID() {
return gl_LocalInvocationID;
}
/**
* @public-api
* Globally unique value across the entire compute dispatch.
* 1D version of getLocalInvocationID(). Provided for convenience.
*/
uint getLocalInvocationIndex() {
return gl_LocalInvocationIndex;
}
/**
* @public-api
* Globally unique value across the entire compute dispatch.
* Short-hand for getWorkGroupID() * getWorkGroupCount() + getLocalInvocationID()
*/
uvec3 getGlobalInvocationID() {
return gl_GlobalInvocationID;
}

4
shaders/src/main.cs Normal file
View File

@@ -0,0 +1,4 @@
void main() {
// Invoke user code
compute();
}

View File

@@ -86,6 +86,8 @@ set(TARGET test_matc)
set(SRCS
tests/test_matc.cpp
tests/test_includer.cpp
tests/TestMaterialCompiler.h
tests/test_compute_material.cpp
tests/MockConfig.cpp
tests/MockConfig.h)

View File

@@ -47,17 +47,20 @@ namespace matc {
static constexpr const char* CONFIG_KEY_MATERIAL= "material";
static constexpr const char* CONFIG_KEY_VERTEX_SHADER = "vertex";
static constexpr const char* CONFIG_KEY_FRAGMENT_SHADER = "fragment";
static constexpr const char* CONFIG_KEY_COMPUTE_SHADER = "compute";
static constexpr const char* CONFIG_KEY_TOOL = "tool";
MaterialCompiler::MaterialCompiler() {
mConfigProcessor[CONFIG_KEY_MATERIAL] = &MaterialCompiler::processMaterial;
mConfigProcessor[CONFIG_KEY_VERTEX_SHADER] = &MaterialCompiler::processVertexShader;
mConfigProcessor[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialCompiler::processFragmentShader;
mConfigProcessor[CONFIG_KEY_COMPUTE_SHADER] = &MaterialCompiler::processComputeShader;
mConfigProcessor[CONFIG_KEY_TOOL] = &MaterialCompiler::ignoreLexeme;
mConfigProcessorJSON[CONFIG_KEY_MATERIAL] = &MaterialCompiler::processMaterialJSON;
mConfigProcessorJSON[CONFIG_KEY_VERTEX_SHADER] = &MaterialCompiler::processVertexShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialCompiler::processFragmentShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_COMPUTE_SHADER] = &MaterialCompiler::processComputeShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_TOOL] = &MaterialCompiler::ignoreLexemeJSON;
}
@@ -109,6 +112,11 @@ bool MaterialCompiler::processFragmentShader(const MaterialLexeme& lexeme,
return true;
}
bool MaterialCompiler::processComputeShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
return MaterialCompiler::processFragmentShader(lexeme, builder);
}
bool MaterialCompiler::ignoreLexeme(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
return true;
@@ -179,6 +187,25 @@ bool MaterialCompiler::processFragmentShaderJSON(const JsonishValue* value,
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialCompiler::processComputeShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'compute' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'compute' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialCompiler::ignoreLexemeJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept {

View File

@@ -50,6 +50,8 @@ private:
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexeme(const MaterialLexeme&, filamat::MaterialBuilder& builder) const noexcept;
bool parseMaterialAsJSON(const char* buffer, size_t size,
@@ -60,6 +62,8 @@ private:
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexemeJSON(const JsonishValue*, filamat::MaterialBuilder& builder) const noexcept;
bool isValidJsonStart(const char* buffer, size_t size) const noexcept;

View File

@@ -15,15 +15,21 @@
*/
#include "ParametersProcessor.h"
#include "backend/DriverEnums.h"
#include <filamat/Enums.h>
#include <private/filament/BufferInterfaceBlock.h>
#include <private/filament/Variant.h>
#include <backend/DriverEnums.h>
#include <math/vec3.h>
#include <algorithm>
#include <iostream>
#include <string_view>
#include <ctype.h>
#include <private/filament/Variant.h>
#include <filamat/Enums.h>
using namespace filamat;
using namespace utils;
@@ -90,24 +96,25 @@ static bool processInterpolation(MaterialBuilder& builder, const JsonishValue& v
* For instance:
* "float" returns 0 and "float" is unmodified
* "float[4]" returns 4 and "float[4]" is changed to "float"
* "float[]" returns -1 and "float[]" is changed to "float"
* "float[3" returns 0 and "float[3" is unmodified
* "float[2]foo" returns 0 and "float[2]foo" is unmodified
* "float[2foo]" returns 0 and "float[2foo]" is unmodified
*/
static size_t extractArraySize(std::string& type) {
size_t start = type.find_first_of('[');
static ssize_t extractArraySize(std::string& type) {
auto start = type.find_first_of('[');
// Not an array
if (start == std::string::npos) {
return 0;
}
size_t end = type.find_first_of(']', start);
auto end = type.find_first_of(']', start);
// If we cannot find ']' or if it's not the last character, return to fail later
if (end == std::string::npos || end != type.length() - 1) {
return 0;
}
// If not all of the characters in the array declaration are digits, return to fail later
// If not all the characters in the array declaration are digits, return to fail later
if (!std::all_of(type.cbegin() + start + 1, type.cbegin() + end,
[](char c) { return isdigit(c); })) {
return 0;
@@ -116,8 +123,13 @@ static size_t extractArraySize(std::string& type) {
// Remove the [...] bit
type.erase(start);
// handle an empty size array: []
if (end - start == 1) {
return -1;
}
// Return the size (we already validated this part of the string contains only digits)
return std::stoul(type.c_str() + start + 1, nullptr);
return (ssize_t)std::stoul(type.c_str() + start + 1, nullptr);
}
static bool processParameter(MaterialBuilder& builder, const JsonishObject& jsonObject) noexcept {
@@ -181,9 +193,9 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
}
if (arraySize == 0) {
builder.parameter(type, precision, nameString.c_str());
builder.parameter(nameString.c_str(), type, precision);
} else {
builder.parameter(type, arraySize, precision, nameString.c_str());
builder.parameter(nameString.c_str(), arraySize, type, precision);
}
} else if (Enums::isValid<SamplerType>(typeString)) {
if (arraySize > 0) {
@@ -198,40 +210,16 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(type, format, precision, nameString.c_str());
builder.parameter(nameString.c_str(), type, format, precision);
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.parameter(type, format, nameString.c_str());
builder.parameter(nameString.c_str(), type, format);
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(type, precision, nameString.c_str());
builder.parameter(nameString.c_str(), type, precision);
} else {
builder.parameter(type, nameString.c_str());
}
} else if (Enums::isValid<SubpassType>(typeString)) {
if (arraySize > 0) {
std::cerr << "parameters: the parameter with name '" << nameString << "'"
<< " is an array of subpasses of size " << arraySize << ". Arrays of subpasses"
<< " are currently not supported." << std::endl;
return false;
}
MaterialBuilder::SubpassType type = Enums::toEnum<SubpassType>(typeString);
if (precisionValue && formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(type, format, precision, nameString.c_str());
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.parameter(type, format, nameString.c_str());
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(type, precision, nameString.c_str());
} else {
builder.parameter(type, nameString.c_str());
builder.parameter(nameString.c_str(), type);
}
} else {
std::cerr << "parameters: the type '" << typeString
@@ -258,6 +246,271 @@ static bool processParameters(MaterialBuilder& builder, const JsonishValue& v) {
return ok;
}
static bool processBufferField(filament::BufferInterfaceBlock::Builder& builder,
const JsonishObject& jsonObject) noexcept {
const JsonishValue* nameValue = jsonObject.getValue("name");
if (!nameValue) {
std::cerr << "buffers: entry without 'name' key." << std::endl;
return false;
}
if (nameValue->getType() != JsonishValue::STRING) {
std::cerr << "buffers: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* typeValue = jsonObject.getValue("type");
if (!typeValue) {
std::cerr << "buffers: entry without key 'type'." << std::endl;
return false;
}
if (typeValue->getType() != JsonishValue::STRING) {
std::cerr << "buffers: type value must be STRING." << std::endl;
return false;
}
const JsonishValue* precisionValue = jsonObject.getValue("precision");
if (precisionValue) {
if (precisionValue->getType() != JsonishValue::STRING) {
std::cerr << "buffers: precision must be a STRING." << std::endl;
return false;
}
auto precisionString = precisionValue->toJsonString();
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
return logEnumIssue("buffers", *precisionString, Enums::map<ParameterPrecision>());
}
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
size_t arraySize = extractArraySize(typeString);
if (Enums::isValid<UniformType>(typeString)) {
MaterialBuilder::UniformType type = Enums::toEnum<UniformType>(typeString);
ParameterPrecision precision = ParameterPrecision::DEFAULT;
if (precisionValue) {
precision = Enums::toEnum<ParameterPrecision>(
precisionValue->toJsonString()->getString());
}
if (arraySize == -1) {
builder.addVariableSizedArray({
{ nameString.data(), nameString.size() }, 0, type, precision });
} else {
builder.add({{
{ nameString.data(), nameString.size() }, uint32_t(arraySize), type, precision } });
}
} else {
std::cerr << "buffers: the type '" << typeString << "' for parameter with name '"
<< nameString << "' is not a valid buffer field type." << std::endl;
return false;
}
return true;
}
static bool processBuffer(MaterialBuilder& builder,
const JsonishObject& jsonObject) noexcept {
filament::BufferInterfaceBlock::Builder bibb;
bibb.target(filament::BufferInterfaceBlock::Target::SSBO);
bibb.alignment(filament::BufferInterfaceBlock::Alignment::std430);
const JsonishValue* nameValue = jsonObject.getValue("name");
if (!nameValue) {
std::cerr << "buffers: entry without 'name' key." << std::endl;
return false;
}
if (nameValue->getType() != JsonishValue::STRING) {
std::cerr << "buffers: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* qualifiersValue = jsonObject.getValue("qualifiers");
if (!qualifiersValue) {
std::cerr << "buffers: entry without key 'qualifiers'." << std::endl;
return false;
}
if (qualifiersValue->getType() != JsonishValue::ARRAY) {
std::cerr << "buffers: qualifiers value must be an ARRAY." << std::endl;
return false;
}
const JsonishValue* fieldsValue = jsonObject.getValue("fields");
if (!fieldsValue) {
std::cerr << "buffers: entry without key 'fields'." << std::endl;
return false;
}
if (fieldsValue->getType() != JsonishValue::ARRAY) {
std::cerr << "buffers: fields value must be an ARRAY." << std::endl;
return false;
}
auto nameString = nameValue->toJsonString()->getString();
bibb.name({ nameString.data(), nameString.size() });
for (auto value : qualifiersValue->toJsonArray()->getElements()) {
if (value->getType() == JsonishValue::Type::STRING) {
using namespace std::literals;
using Qualifier = filament::BufferInterfaceBlock::Qualifier;
auto qualifierString = value->toJsonString()->getString();
if (qualifierString == "coherent"sv) {
bibb.qualifier(Qualifier::COHERENT);
} else if (qualifierString == "writeonly"sv) {
bibb.qualifier(Qualifier::WRITEONLY);
} else if (qualifierString == "readonly"sv) {
bibb.qualifier(Qualifier::READONLY);
} else if (qualifierString == "volatile"sv) {
bibb.qualifier(Qualifier::VOLATILE);
} else if (qualifierString == "restrict"sv) {
bibb.qualifier(Qualifier::RESTRICT);
}
continue;
}
std::cerr << "buffers: qualifiers must be an array of STRINGs." << std::endl;
return false;
}
bool ok = true;
for (auto value : fieldsValue->toJsonArray()->getElements()) {
if (bibb.hasVariableSizeArray()) {
std::cerr << "buffers: a variable size array must be the only and last field." << std::endl;
return false;
}
if (value->getType() == JsonishValue::Type::OBJECT) {
ok &= processBufferField(bibb, *value->toJsonObject());
continue;
}
std::cerr << "buffers: fields must be an array of OBJECTs." << std::endl;
return false;
}
builder.buffer(bibb.build());
return ok;
}
static bool processBuffers(MaterialBuilder& builder, const JsonishValue& v) {
auto jsonArray = v.toJsonArray();
bool ok = true;
for (auto value : jsonArray->getElements()) {
if (value->getType() == JsonishValue::Type::OBJECT) {
ok &= processBuffer(builder, *value->toJsonObject());
continue;
}
std::cerr << "buffers must be an array of OBJECTs." << std::endl;
return false;
}
return ok;
}
static bool processSubpass(MaterialBuilder& builder, const JsonishObject& jsonObject) noexcept {
const JsonishValue* typeValue = jsonObject.getValue("type");
if (!typeValue) {
std::cerr << "subpasses: entry without key 'type'." << std::endl;
return false;
}
if (typeValue->getType() != JsonishValue::STRING) {
std::cerr << "subpasses: type value must be STRING." << std::endl;
return false;
}
const JsonishValue* nameValue = jsonObject.getValue("name");
if (!nameValue) {
std::cerr << "subpasses: entry without 'name' key." << std::endl;
return false;
}
if (nameValue->getType() != JsonishValue::STRING) {
std::cerr << "subpasses: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* precisionValue = jsonObject.getValue("precision");
if (precisionValue) {
if (precisionValue->getType() != JsonishValue::STRING) {
std::cerr << "subpasses: precision must be a STRING." << std::endl;
return false;
}
auto precisionString = precisionValue->toJsonString();
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
return logEnumIssue("subpasses", *precisionString, Enums::map<ParameterPrecision>());
}
}
const JsonishValue* formatValue = jsonObject.getValue("format");
if (formatValue) {
if (formatValue->getType() != JsonishValue::STRING) {
std::cerr << "subpasses: format must be a STRING." << std::endl;
return false;
}
auto formatString = formatValue->toJsonString();
if (!Enums::isValid<SamplerFormat>(formatString->getString())){
return logEnumIssue("subpasses", *formatString, Enums::map<SamplerFormat>());
}
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
size_t arraySize = extractArraySize(typeString);
if (Enums::isValid<SubpassType>(typeString)) {
if (arraySize > 0) {
std::cerr << "subpasses: the parameter with name '" << nameString << "'"
<< " is an array of subpasses of size " << arraySize << ". Arrays of subpasses"
<< " are currently not supported." << std::endl;
return false;
}
MaterialBuilder::SubpassType type = Enums::toEnum<SubpassType>(typeString);
if (precisionValue && formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.subpass(type, format, precision, nameString.c_str());
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.subpass(type, format, nameString.c_str());
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.subpass(type, precision, nameString.c_str());
} else {
builder.subpass(type, nameString.c_str());
}
} else {
std::cerr << "subpasses: the type '" << typeString
<< "' for parameter with name '" << nameString << "' is neither a valid uniform "
<< "type nor a valid sampler type." << std::endl;
return false;
}
return true;
}
static bool processSubpasses(MaterialBuilder& builder, const JsonishValue& v) {
auto jsonArray = v.toJsonArray();
bool ok = true;
for (auto value : jsonArray->getElements()) {
if (value->getType() == JsonishValue::Type::OBJECT) {
ok &= processSubpass(builder, *value->toJsonObject());
continue;
}
std::cerr << "subpasses must be an array of OBJECTs." << std::endl;
return false;
}
return ok;
}
static bool processVariables(MaterialBuilder& builder, const JsonishValue& value) {
const JsonishArray* jsonArray = value.toJsonArray();
const auto& elements = jsonArray->getElements();
@@ -417,6 +670,36 @@ static bool processFeatureLevel(MaterialBuilder& builder, const JsonishValue& va
return true;
}
static bool processGroupSizes(MaterialBuilder& builder, const JsonishValue& v) {
auto jsonArray = v.toJsonArray();
filament::math::uint3 groupSize{ 1, 1, 1 };
size_t index = 0;
for (auto value : jsonArray->getElements()) {
if (index >= 3) {
std::cerr << "groupSize: must be an array no larger than 3" << std::endl;
return false;
}
if (value->getType() == JsonishValue::Type::NUMBER) {
JsonishNumber const* const number = value->toJsonNumber();
float aFloat = number->getFloat();
if (aFloat > 0 && floor(aFloat) == aFloat) {
groupSize[index] = uint32_t(floor(aFloat));
} else {
std::cerr << "groupSize: invalid value " << aFloat << std::endl;
return false;
}
index++;
continue;
}
std::cerr << "groupSize must be an array of NUMBERs." << std::endl;
return false;
}
builder.groupSize(groupSize);
return true;
}
static bool processOutput(MaterialBuilder& builder, const JsonishObject& jsonObject) noexcept {
const JsonishValue* nameValue = jsonObject.getValue("name");
@@ -670,6 +953,7 @@ static bool processDomain(MaterialBuilder& builder, const JsonishValue& value) {
static const std::unordered_map<std::string, MaterialBuilder::MaterialDomain> strToEnum {
{ "surface", MaterialBuilder::MaterialDomain::SURFACE },
{ "postprocess", MaterialBuilder::MaterialDomain::POST_PROCESS },
{ "compute", MaterialBuilder::MaterialDomain::COMPUTE },
};
auto jsonString = value.toJsonString();
if (!isStringValidEnum(strToEnum, jsonString->getString())) {
@@ -770,6 +1054,8 @@ ParametersProcessor::ParametersProcessor() {
mParameters["name"] = { &processName, Type::STRING };
mParameters["interpolation"] = { &processInterpolation, Type::STRING };
mParameters["parameters"] = { &processParameters, Type::ARRAY };
mParameters["buffers"] = { &processBuffers, Type::ARRAY };
mParameters["subpasses"] = { &processSubpasses, Type::ARRAY };
mParameters["variables"] = { &processVariables, Type::ARRAY };
mParameters["requires"] = { &processRequires, Type::ARRAY };
mParameters["blending"] = { &processBlending, Type::STRING };
@@ -805,6 +1091,7 @@ ParametersProcessor::ParametersProcessor() {
mParameters["quality"] = { &processQuality, Type::STRING };
mParameters["customSurfaceShading"] = { &processCustomSurfaceShading, Type::BOOL };
mParameters["featureLevel"] = { &processFeatureLevel, Type::NUMBER };
mParameters["groupSize"] = { &processGroupSizes, Type::ARRAY };
}
bool ParametersProcessor::process(MaterialBuilder& builder, const JsonishObject& jsonObject) {

View File

@@ -32,10 +32,9 @@ public:
bool close() noexcept override;
private:
class NullBuffer : public std::streambuf
{
class NullBuffer : public std::streambuf {
public:
int overflow(int c) { return c; }
int overflow(int c) override { return c; }
};
NullBuffer mNull;
std::ostream mNullStream;
@@ -44,7 +43,7 @@ private:
class MockInput : public matc::Config::Input {
public:
MockInput(uint8_t* data, size_t size);
~MockInput();
~MockInput() override;
ssize_t open() noexcept override;
std::unique_ptr<const char[]> read() noexcept override;
bool close() noexcept override;

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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.
*/
#ifndef TNT_TESTMATERIALCOMPILER_H
#define TNT_TESTMATERIALCOMPILER_H
#include <stddef.h>
#include <matc/MaterialCompiler.h>
class TestMaterialCompiler {
public:
explicit TestMaterialCompiler(const matc::MaterialCompiler& materialCompiler) :
mMaterialCompiler(materialCompiler) {}
bool parseMaterial(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterial(buffer, size, builder);
}
bool parseMaterialAsJSON(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterialAsJSON(buffer, size, builder);
}
private:
const matc::MaterialCompiler& mMaterialCompiler;
};
#endif //TNT_TESTMATERIALCOMPILER_H

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 <gtest/gtest.h>
#include <matc/MaterialCompiler.h>
#include "TestMaterialCompiler.h"
#include <utils/JobSystem.h>
static std::string_view jsonMaterialSourceSimple(R"(
material {
name: test_compute,
domain: compute,
groupSize: [8, 8, 1],
parameters: [
]
}
compute {
void compute() {
}
}
)");
TEST(TestComputeMaterial, JsonMaterialCompilerSimple) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
filamat::MaterialBuilder::init();
filamat::MaterialBuilder builder;
bool result = compiler.parseMaterial(jsonMaterialSourceSimple.data(), jsonMaterialSourceSimple.size(), builder);
EXPECT_TRUE(result);
utils::JobSystem js;
js.adopt();
auto package = builder.build(js);
EXPECT_TRUE(package.isValid());
js.emancipate();
filamat::MaterialBuilder::shutdown();
}

View File

@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include "MockConfig.h"
#include "TestMaterialCompiler.h"
#include <matc/MaterialCompiler.h>
#include <matc/MaterialLexer.h>
@@ -26,26 +27,7 @@
class MaterialLexer: public ::testing::Test {
protected:
MaterialLexer() = default;
~MaterialLexer() = default;
};
class TestMaterialCompiler {
public:
explicit TestMaterialCompiler(const matc::MaterialCompiler& materialCompiler) :
mMaterialCompiler(materialCompiler) {}
bool parseMaterial(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterial(buffer, size, builder);
}
bool parseMaterialAsJSON(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterialAsJSON(buffer, size, builder);
}
private:
const matc::MaterialCompiler& mMaterialCompiler;
~MaterialLexer() override = default;
};
static std::string materialSource(R"(

View File

@@ -114,19 +114,19 @@ static void license() {
static int handleArguments(int argc, char* argv[], Config* config) {
static constexpr const char* OPTSTR = "hla:g:s:v:b:m:b:w:xyz";
static const struct option OPTIONS[] = {
{ "help", no_argument, 0, 'h' },
{ "license", no_argument, 0, 'l' },
{ "analyze-spirv", required_argument, 0, 'a' },
{ "print-glsl", required_argument, 0, 'g' },
{ "print-spirv", required_argument, 0, 's' },
{ "print-vkglsl", required_argument, 0, 'v' },
{ "print-metal", required_argument, 0, 'm' },
{ "print-dic-glsl", no_argument, 0, 'x' },
{ "print-dic-metal", no_argument, 0, 'y' },
{ "print-dic-vk", no_argument, 0, 'z' },
{ "dump-binary", required_argument, 0, 'b' },
{ "web-server", required_argument, 0, 'w' },
{ 0, 0, 0, 0 } // termination of the option list
{ "help", no_argument, nullptr, 'h' },
{ "license", no_argument, nullptr, 'l' },
{ "analyze-spirv", required_argument, nullptr, 'a' },
{ "print-glsl", required_argument, nullptr, 'g' },
{ "print-spirv", required_argument, nullptr, 's' },
{ "print-vkglsl", required_argument, nullptr, 'v' },
{ "print-metal", required_argument, nullptr, 'm' },
{ "print-dic-glsl", no_argument, nullptr, 'x' },
{ "print-dic-metal", no_argument, nullptr, 'y' },
{ "print-dic-vk", no_argument, nullptr, 'z' },
{ "dump-binary", required_argument, nullptr, 'b' },
{ "web-server", required_argument, nullptr, 'w' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
@@ -194,7 +194,8 @@ static bool read(const filaflat::ChunkContainer& container, filamat::ChunkType t
return false;
}
filaflat::Unflattener unflattener(container.getChunkStart(type), container.getChunkEnd(type));
auto [start, end] = container.getChunkRange(type);
filaflat::Unflattener unflattener(start, end);
return unflattener.read(value);
}
@@ -219,7 +220,7 @@ static std::map<int, std::string> transpileSpirvToLines(const std::vector<uint32
emitOptions.vulkan_semantics = true;
emitOptions.emit_line_directives = true;
CompilerGLSL glslCompiler(move(spirv));
CompilerGLSL glslCompiler(spirv);
glslCompiler.set_common_options(emitOptions);
std::string transpiled = glslCompiler.compile();
@@ -369,7 +370,7 @@ static void disassembleSpirv(const std::vector<uint32_t>& spirv, bool analyze) {
}
}
static void dumpSpirvBinary(const std::vector<uint32_t>& spirv, std::string filename) {
static void dumpSpirvBinary(const std::vector<uint32_t>& spirv, const std::string& filename) {
std::ofstream out(filename, std::ofstream::binary);
out.write((const char*) spirv.data(), spirv.size() * 4);
std::cout << "Binary SPIR-V dumped to " << filename << std::endl;
@@ -426,7 +427,7 @@ static bool parseChunks(Config config, void* data, size_t size) {
const auto& item = info[config.shaderIndex];
parser.getShader(item.shaderModel, item.variant, item.pipelineStage, content);
// Casted to char* to print as a string rather than hex value.
// Cast to char* to print as a string rather than hex value.
std::cout << (const char*) content.data();
return true;
@@ -510,8 +511,8 @@ static bool parseChunks(Config config, void* data, size_t size) {
return false;
}
for (size_t i = 0; i < dictionary.size(); i++) {
std::cout << (const char*)dictionary[i].data() << std::endl;
for (auto const& i : dictionary) {
std::cout << (const char*)i.data() << std::endl;
}
return true;