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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
95
filament/backend/test/ComputeTest.cpp
Normal file
95
filament/backend/test/ComputeTest.cpp
Normal 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);
|
||||
}
|
||||
51
filament/backend/test/ComputeTest.h
Normal file
51
filament/backend/test/ComputeTest.h
Normal 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
|
||||
154
filament/backend/test/test_ComputeBasic.cpp
Normal file
154
filament/backend/test/test_ComputeBasic.cpp
Normal 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);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,7 +30,9 @@ material {
|
||||
{
|
||||
type : float4,
|
||||
name : vignetteColor
|
||||
},
|
||||
}
|
||||
],
|
||||
subpasses : [
|
||||
{
|
||||
type : subpassInput,
|
||||
format : float,
|
||||
|
||||
@@ -7,7 +7,9 @@ material {
|
||||
type : float,
|
||||
name : direction,
|
||||
precision: low
|
||||
},
|
||||
}
|
||||
],
|
||||
subpasses : [
|
||||
{
|
||||
type : subpassInput,
|
||||
format : float,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
186
libs/filabridge/include/private/filament/BufferInterfaceBlock.h
Normal file
186
libs/filabridge/include/private/filament/BufferInterfaceBlock.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
233
libs/filabridge/src/BufferInterfaceBlock.cpp
Normal file
233
libs/filabridge/src/BufferInterfaceBlock.cpp
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -37,6 +37,8 @@ std::vector<Variant> determineSurfaceVariants(
|
||||
|
||||
std::vector<Variant> determinePostProcessVariants();
|
||||
|
||||
std::vector<Variant> determineComputeVariants();
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
#endif
|
||||
|
||||
@@ -77,6 +77,7 @@ void SamplerBindingMap::init(MaterialDomain materialDomain,
|
||||
}
|
||||
break;
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
case MaterialDomain::COMPUTE:
|
||||
processSamplerGroup(SamplerBindingPoints::PER_MATERIAL_INSTANCE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 "--";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
24
shaders/src/common_defines.glsl
Normal file
24
shaders/src/common_defines.glsl
Normal 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
|
||||
@@ -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
45
shaders/src/getters.cs
Normal 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
4
shaders/src/main.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
void main() {
|
||||
// Invoke user code
|
||||
compute();
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
43
tools/matc/tests/TestMaterialCompiler.h
Normal file
43
tools/matc/tests/TestMaterialCompiler.h
Normal 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
|
||||
60
tools/matc/tests/test_compute_material.cpp
Normal file
60
tools/matc/tests/test_compute_material.cpp
Normal 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();
|
||||
}
|
||||
|
||||
@@ -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"(
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user