Merge branch 'rc/1.51.0' into release

This commit is contained in:
Benjamin Doherty
2024-03-17 13:12:40 -07:00
59 changed files with 873 additions and 616 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.50.6'
implementation 'com.google.android.filament:filament-android:1.51.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.50.6'
pod 'Filament', '~> 1.51.0'
```
### Snapshots
+4
View File
@@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.51.0
- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
## v1.50.6
- Add new API `SwapChain::getFrameScheduledCallback`
@@ -654,7 +654,7 @@ public class Engine {
*/
@NonNull
public SwapChain createSwapChain(@NonNull Object surface) {
return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT);
}
/**
@@ -662,15 +662,15 @@ public class Engine {
*
* @param surface on Android, <b>must be</b> an instance of {@link android.view.Surface}
*
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
@@ -688,21 +688,22 @@ public class Engine {
*
* @param width width of the rendering buffer
* @param height height of the rendering buffer
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
* @see SwapChain#CONFIG_DEFAULT
* @see SwapChain#CONFIG_TRANSPARENT
* @see SwapChain#CONFIG_READABLE
* @see SwapChainFlags#CONFIG_DEFAULT
* @see SwapChainFlags#CONFIG_TRANSPARENT
* @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
public SwapChain createSwapChain(int width, int height, long flags) {
if (width >= 0 && height >= 0) {
long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
long nativeSwapChain =
nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
return new SwapChain(nativeSwapChain, null);
}
@@ -714,11 +715,12 @@ public class Engine {
*
* @param surface a properly initialized {@link NativeSurface}
*
* @param flags configuration flags, see {@link SwapChain}
* @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created
* @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be
* created
*/
@NonNull
public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) {
@@ -68,72 +68,6 @@ public class SwapChain {
private final Object mSurface;
private long mNativeObject;
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
/**
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
@@ -145,17 +79,19 @@ public class SwapChain {
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
* @see SwapChainFlags#CONFIG_PROTECTED_CONTENT
*/
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
return nIsProtectedContentSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
* @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise.
* @see SwapChainFlags#CONFIG_SRGB_COLORSPACE
*/
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2024 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.
*/
package com.google.android.filament;
// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend
// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java
// classes.
/**
* Flags that a <code>SwapChain</code> can be created with to control behavior.
*
* @see Engine#createSwapChain
* @see Engine#createSwapChainFromNativeSurface
*/
public final class SwapChainFlags {
public static final long CONFIG_DEFAULT = 0x0;
/**
* This flag indicates that the <code>SwapChain</code> must be allocated with an
* alpha-channel.
*/
public static final long CONFIG_TRANSPARENT = 0x1;
/**
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
* for reading back render results. This config must be set when creating
* any <code>SwapChain</code> that will be used as the source for a blit operation.
*
* @see Renderer#copyFrame
*/
public static final long CONFIG_READABLE = 0x2;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
public static final long CONFIG_ENABLE_XCB = 0x4;
/**
* Indicates that the SwapChain must automatically perform linear to sRGB encoding.
*
* This flag is ignored if isSRGBSwapChainSupported() is false.
*
* When using this flag, post-processing should be disabled.
*
* @see SwapChain#isSRGBSwapChainSupported
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_SRGB_COLORSPACE = 0x10;
/**
* Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
*
* This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
* the SwapChain (when post-processing is disabled).
*
* The specific format of the stencil buffer depends on platform support. The following pixel
* formats are tried, in order of preference:
*
* Depth only (without CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F
* - DEPTH24
*
* Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
* - DEPTH32F_STENCIL8
* - DEPTH24F_STENCIL8
*
* Note that enabling the stencil buffer may hinder depth precision and should only be used if
* necessary.
*
* @see View#setStencilBufferEnabled
* @see View#setPostProcessingEnabled
*/
public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
/**
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
}
@@ -27,7 +27,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.filament.SwapChain;
import com.google.android.filament.SwapChainFlags;
/**
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
@@ -538,7 +538,7 @@ public class UiHelper {
* the options set on this UiHelper.
*/
public long getSwapChainFlags() {
return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT;
}
/**
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.50.6
VERSION_NAME=1.51.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+20 -22
View File
@@ -252,8 +252,8 @@ set(MATERIAL_SRCS
)
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
src/materials/defaultMaterial.mat
src/materials/skybox.mat
)
# Embed the binary resource blob for materials.
@@ -316,32 +316,30 @@ foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
foreach (mat_src ${MATERIAL_FL0_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
string(REGEX REPLACE "[.]filamat$" "0.filamat" output_path0 ${output_path})
add_custom_command(
OUTPUT ${output_path} ${output_path0}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path0} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${fullname} to ${output_path} and ${output_path0}"
)
list(APPEND MATERIAL_BINS ${output_path0})
else ()
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
COMMENT "Compiling material ${fullname} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach ()
endif ()
endif ()
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
# Additional dependencies on included files for materials
+18 -2
View File
@@ -1186,11 +1186,27 @@ struct StencilState {
//! Stencil operations for front-facing polygons
StencilOperations front = {
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
//! Stencil operations for back-facing polygons
StencilOperations back = {
.stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
.stencilFunc = StencilFunction::A,
.stencilOpStencilFail = StencilOperation::KEEP,
.padding0 = 0,
.stencilOpDepthFail = StencilOperation::KEEP,
.stencilOpDepthStencilPass = StencilOperation::KEEP,
.padding1 = 0,
.ref = 0,
.readMask = 0xff,
.writeMask = 0xff };
//! Whether stencil-buffer writes are enabled
bool stencilWrite = false;
@@ -29,10 +29,13 @@ namespace filament::backend {
//! \privatesection
struct PipelineState {
Handle<HwProgram> program;
RasterState rasterState;
StencilState stencilState;
PolygonOffset polygonOffset;
Handle<HwProgram> program; // 4
Handle<HwVertexBufferInfo> vertexBufferInfo; // 4
RasterState rasterState; // 4
StencilState stencilState; // 12
PolygonOffset polygonOffset; // 8
PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
uint8_t padding[3] = {}; // 3
};
} // namespace filament::backend
+1 -1
View File
@@ -56,7 +56,7 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
* Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
};
@@ -493,6 +493,17 @@ DECL_DRIVER_API_N(blit,
math::uint2, srcOrigin,
math::uint2, size)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(draw2,
uint32_t, indexOffset,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,
@@ -500,6 +511,7 @@ DECL_DRIVER_API_N(draw,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
@@ -37,9 +37,9 @@
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
namespace filament::backend {
@@ -99,6 +99,7 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
DepthStencilStateCache depthStencilStateCache;
+64 -30
View File
@@ -48,24 +48,26 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
// MetalRenderPrimitive : 24 many
// MetalVertexBuffer : 32 moderate
// -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 144 moderate
// MetalProgram : 152 moderate
// MetalTexture : 152 moderate
// MetalVertexBuffer : 152 moderate
// -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalRenderPrimitive : 584 many
// -- less than or equal to 592 bytes
// MetalVertexBufferInfo : 552 moderate
// -- less than or equal to 552 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
@@ -178,7 +180,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
}
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile
? MetalShaderCompiler::Mode::SYNCHRONOUS
: MetalShaderCompiler::Mode::ASYNCHRONOUS;
mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode);
mContext->shaderCompiler->init();
#if defined(FILAMENT_METAL_PROFILING)
@@ -786,7 +791,7 @@ bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType)
}
bool MetalDriver::isParallelShaderCompileSupported() {
return true;
return mContext->shaderCompiler->isParallelShaderCompileSupported();
}
bool MetalDriver::isDepthStencilResolveSupported() {
@@ -1602,11 +1607,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void MetalDriver::bindPipeline(PipelineState ps) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"Attempted to draw without a valid command encoder.");
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
"bindPipeline() without a valid command encoder.");
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(ps.vertexBufferInfo);
auto program = handle_cast<MetalProgram>(ps.program);
const auto& rs = ps.rasterState;
@@ -1620,7 +1627,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
return;
}
functions.validate();
ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1647,7 +1654,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
.vertexDescription = primitive->vertexDescription,
.vertexDescription = vbi->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
colorPixelFormat[1],
@@ -1741,20 +1748,6 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Bind sampler groups (argument buffers).
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
@@ -1785,19 +1778,29 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
}
}
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"bindRenderPrimitive() without a valid command encoder.");
// Bind the user vertex buffers.
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t maxBufferIndex = 0;
MetalRenderPrimitive const* const primitive = handle_cast<MetalRenderPrimitive>(rph);
MetalVertexBufferInfo const* const vbi =
handle_cast<MetalVertexBufferInfo>(primitive->vertexBuffer->vbih);
mContext->currentRenderPrimitive = rph;
auto vb = primitive->vertexBuffer;
for (auto m : primitive->bufferMapping) {
for (auto m : vbi->bufferMapping) {
assert_invariant(
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
}
@@ -1812,6 +1815,27 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
[mContext->currentRenderPassEncoder setVertexBytes:bytes
length:16
atIndex:ZERO_VERTEX_BUFFER_BINDING];
}
void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"draw() without a valid command encoder.");
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
@@ -1825,6 +1849,16 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
instanceCount:instanceCount];
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
MetalRenderPrimitive const* const rp = handle_cast<MetalRenderPrimitive>(rph);
ps.primitiveType = rp->type;
ps.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(ps);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
+15 -17
View File
@@ -149,7 +149,21 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
AttributeArray attributes;
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
struct Entry {
uint8_t sourceBufferIndex = 0;
uint8_t stride = 0;
// maps to ->
uint8_t bufferArgumentIndex = 0;
Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
: sourceBufferIndex(sourceBufferIndex),
stride(stride),
bufferArgumentIndex(bufferArgumentIndex) {}
};
utils::FixedCapacityVector<Entry> bufferMapping;
};
struct MetalVertexBuffer : public HwVertexBuffer {
@@ -176,22 +190,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
struct Entry {
uint8_t sourceBufferIndex = 0;
uint8_t stride = 0;
// maps to ->
uint8_t bufferArgumentIndex = 0;
Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
: sourceBufferIndex(sourceBufferIndex),
stride(stride),
bufferArgumentIndex(bufferArgumentIndex) {}
};
utils::FixedCapacityVector<Entry> bufferMapping;
};
class MetalProgram : public HwProgram {
+22 -22
View File
@@ -343,27 +343,9 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {
}
bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
const size_t attributeCount = vbi->attributes.size();
const size_t maxAttributeCount = attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -403,8 +385,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
};
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vbi->attributes[attributeIndex];
for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) {
const auto& attribute = attributes[attributeIndex];
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
// to read from missing vertex attributes.
@@ -431,6 +413,24 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive() {
}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
@@ -41,6 +41,11 @@ class MetalShaderCompiler {
struct MetalProgramToken;
public:
enum class Mode {
SYNCHRONOUS, // synchronous shader compilation
ASYNCHRONOUS // asynchronous shader compilation
};
class MetalFunctionBundle {
public:
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
@@ -110,7 +115,7 @@ public:
using program_token_t = std::shared_ptr<MetalProgramToken>;
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver);
explicit MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode);
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
@@ -120,15 +125,15 @@ public:
void init() noexcept;
void terminate() noexcept;
// Creates a program asynchronously
bool isParallelShaderCompileSupported() const noexcept;
// Creates a program, either synchronously or asynchronously, depending on the Mode
// MetalShaderCompiler was constructed with.
program_token_t createProgram(utils::CString const& name, Program&& program);
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
MetalFunctionBundle getProgram(program_token_t& token);
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
static void terminate(program_token_t& token);
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -138,6 +143,7 @@ private:
CompilerThreadPool mCompilerThreadPool;
id<MTLDevice> mDevice;
CallbackManager mCallbackManager;
Mode mMode;
};
} // namespace filament::backend
@@ -70,22 +70,31 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver)
MetalShaderCompiler::MetalShaderCompiler(id<MTLDevice> device, MetalDriver& driver, Mode mode)
: mDevice(device),
mCallbackManager(driver) {
mCallbackManager(driver),
mMode(mode) {
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 1;
mCompilerThreadPool.init(poolSize, []() {}, []() {});
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.init(poolSize, []() {}, []() {});
}
}
void MetalShaderCompiler::terminate() noexcept {
mCompilerThreadPool.terminate();
if (mMode == Mode::ASYNCHRONOUS) {
mCompilerThreadPool.terminate();
}
mCallbackManager.terminate();
}
bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
return mMode == Mode::ASYNCHRONOUS;
}
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
const Program& program, id<MTLDevice> device) {
std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
@@ -180,14 +189,26 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
token->handle = mCallbackManager.get();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
switch (mMode) {
case Mode::ASYNCHRONOUS: {
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
mCompilerThreadPool.queue(priorityQueue, token,
[this, name, device = mDevice, program = std::move(program), token]() {
MetalFunctionBundle compiledProgram = compileProgram(program, device);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
token->set(compiledProgram);
mCallbackManager.put(token->handle);
});
break;
}
case Mode::SYNCHRONOUS: {
MetalFunctionBundle compiledProgram = compileProgram(program, mDevice);
token->set(compiledProgram);
mCallbackManager.put(token->handle);
break;
}
}
return token;
}
@@ -195,35 +216,23 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
assert_invariant(token);
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
if (mMode == Mode::ASYNCHRONOUS) {
if (!token->isReady()) {
auto job = mCompilerThreadPool.dequeue(token);
if (job) {
job();
}
}
}
// The job isn't guaranteed to have finished yet. We may have failed to dequeue it above,
// which means it's currently running. In that case get() will block until it finishes.
MetalShaderCompiler::MetalFunctionBundle program = token->get();
token = nullptr;
return program;
}
/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
assert_invariant(token);
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
if (!job) {
// The job is being executed right now (or has already executed).
token->wait();
} else {
// The job has not executed yet.
token->compiler.mCallbackManager.put(token->handle);
}
token.reset();
}
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
mCallbackManager.setCallback(handler, callback, user);
+9
View File
@@ -350,6 +350,15 @@ void NoopDriver::blit(
math::uint2 size) {
}
void NoopDriver::bindPipeline(PipelineState pipelineState) {
}
void NoopDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
+34 -18
View File
@@ -94,10 +94,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
// GLRenderPrimitive : 20 many
// GLFence : 24 few
// -- less than or equal 24 bytes
// GLRenderPrimitive : 32 many
// GLBufferObject : 32 many
// -- less than or equal 32 bytes
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// -- less than or equal 64 bytes
@@ -518,10 +518,12 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
GLIndexBuffer const* const ib = handle_cast<const GLIndexBuffer*>(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive*>(rph);
GLVertexBuffer* const vb = handle_cast<GLVertexBuffer*>(vbh);
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
rp->vbih = vb->vbih;
// create a name for this VAO in the current context
gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]);
@@ -3784,39 +3786,42 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void OpenGLDriver::bindPipeline(PipelineState state) {
DEBUG_MARKER()
auto& gl = mContext;
setRasterState(state.rasterState);
setStencilState(state.stencilState);
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);
}
bool const success = useProgram(p);
if (UTILS_UNLIKELY(!success)) {
// Avoid fatal (or cascading) errors 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.
return;
}
void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
DEBUG_MARKER()
auto& gl = mContext;
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
if (UTILS_UNLIKELY(!vb)) {
mBoundRenderPrimitive = nullptr;
return;
}
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
gl.bindVertexArray(&rp->gl);
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
updateVertexArrayObject(rp, glvb);
setRasterState(state.rasterState);
setStencilState(state.stencilState);
mBoundRenderPrimitive = rp;
}
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
return;
}
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
@@ -3842,6 +3847,17 @@ void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
DEBUG_MARKER()
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
@@ -142,6 +142,7 @@ public:
struct GLRenderPrimitive : public HwRenderPrimitive {
using HwRenderPrimitive::HwRenderPrimitive;
OpenGLContext::RenderPrimitive gl;
Handle<HwVertexBufferInfo> vbih;
};
struct GLTexture : public HwTexture {
@@ -366,6 +367,10 @@ private:
GLboolean mRenderPassDepthWrite{};
GLboolean mRenderPassStencilWrite{};
GLRenderPrimitive const* mBoundRenderPrimitive = nullptr;
bool mValidProgram = false;
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
@@ -14,21 +14,34 @@
* limitations under the License.
*/
#include <backend/AcquiredImage.h>
#include <backend/Platform.h>
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include "opengl/GLUtils.h"
#include "ExternalStreamManagerAndroid.h"
#include <android/api-level.h>
#include <android/hardware_buffer.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/Log.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <sys/system_properties.h>
#include <jni.h>
#include <new>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
// We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
@@ -165,14 +178,28 @@ int PlatformEGLAndroid::getOSVersion() const noexcept {
AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept {
// Convert the AHardwareBuffer to EGLImage.
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image);
AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image;
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
if (!clientBuffer) {
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
return {};
}
// Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT.
EGLint attrs[] = { EGL_NONE, EGL_NONE };
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
PlatformEGL::Config attributes;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(pHardwareBuffer, &desc);
bool const isProtectedContent =
desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
if (isProtectedContent) {
attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
}
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
if (eglImage == EGL_NO_IMAGE_KHR) {
slog.e << "eglCreateImageKHR returned no image." << io::endl;
return {};
@@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() {
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
<< utils::io::flush;
utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mGroupMarkers->pop();
#endif
+14 -6
View File
@@ -63,11 +63,12 @@
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE | \
FVK_DEBUG_GROUP_MARKERS
FVK_DEBUG_SYSTRACE
#define FVK_DEBUG_CORRECTNESS \
FVK_DEBUG_VALIDATION | \
@@ -80,14 +81,12 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
#else
#define FVK_DEBUG_FLAGS 0
#endif
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
// Group marker only works only if validation or debug utils is enabled since it uses
// vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT
@@ -107,6 +106,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end dependcy checks
// Shorthand for combination of enabled debug flags
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
#else
#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
#endif
// end shorthands
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
+95 -56
View File
@@ -264,26 +264,28 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// VulkanSamplerGroup : 24 few
// VulkanSamplerGroup : 16 few
// HwStream : 24 few
// VulkanFence : 40 few
// VulkanProgram : 40 moderate
// VulkanIndexBuffer : 72 moderate
// VulkanBufferObject : 72 many
// -- less than or equal 80 bytes
// VulkanRenderPrimitive : 104 many
// VulkanSwapChain : 112 few
// VulkanTimerQuery : 168 few
// -- less than or equal 176 bytes
// VulkanTexture : 232 moderate
// VulkanVertexBuffer : 312 moderate
// VulkanRenderTarget : 320 few
// -- less than or equal to 320 bytes
// VulkanFence : 32 few
// VulkanProgram : 32 moderate
// VulkanIndexBuffer : 64 moderate
// VulkanBufferObject : 64 many
// -- less than or equal 64 bytes
// VulkanRenderPrimitive : 72 many
// VulkanVertexBufferInfo : 96 moderate
// VulkanSwapChain : 104 few
// VulkanTimerQuery : 160 few
// -- less than or equal 160 bytes
// VulkanVertexBuffer : 192 moderate
// VulkanTexture : 224 moderate
// VulkanRenderTarget : 312 few
// -- less than or equal to 312 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
@@ -359,6 +361,11 @@ void VulkanDriver::collectGarbage() {
mCommands->gc();
mStagePool.gc();
mFramebufferCache.gc();
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceAllocator.print();
#endif
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -411,9 +418,9 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph,
&mResourceAllocator, pt, vbh, ibh);
mResourceManager.acquire(rp);
VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -982,7 +989,6 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto vb = mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh);
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
vb->setBuffer(mResourceAllocator, bo, index);
}
@@ -1460,14 +1466,6 @@ void VulkanDriver::nextSubpass(int) {
}
}
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive->setBuffers(mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh),
mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh));
primitive->setPrimitiveType(pt);
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
@@ -1713,14 +1711,13 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void VulkanDriver::bindPipeline(PipelineState pipelineState) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
const VulkanVertexBufferInfo& vbi =
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
Handle<HwProgram> programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
@@ -1728,15 +1725,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
commands->acquire(program);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1762,20 +1750,19 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
.depthBiasSlopeFactor = depthOffset.slope
};
// unfortunately in Vulkan the topology is per pipeline
VkPrimitiveTopology const topology =
VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType);
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->attributes.size();
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions();
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
mPipelineCache.bindPrimitiveTopology(topology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount());
// Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding),
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
@@ -1787,6 +1774,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto const& bindingToName = program->getBindingToName();
#endif
UTILS_NOUNROLL
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
uint16_t const indexPair = bindingToSamplerIndex[binding];
@@ -1819,15 +1810,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
utils::slog.w << " at binding point " << +binding << utils::io::endl;
#endif
texture = mEmptyTexture.get();
}
SamplerParams const& samplerParams = boundSampler->s;
VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
reinterpret_cast<uint64_t>(vksampler), bindingToName[binding].c_str());
#endif
VkImageView imageView = VK_NULL_HANDLE;
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -1851,19 +1848,33 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
// message since the validation layers already do so.
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
return;
}
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
FVK_SYSTRACE_END();
}
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindRenderPrimitive");
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
commands->acquire(prim.indexBuffer);
commands->acquire(prim.vertexBuffer);
// This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->getAttributeCount();
VkDeviceSize const* offsets = vbi->getOffsets();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
@@ -1872,15 +1883,43 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw2");
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
// message since the validation layers already do so.
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
return;
}
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
state.primitiveType = rp->type;
state.vertexBufferInfo = rp->vertexBuffer->vbih;
bindPipeline(state);
bindRenderPrimitive(rph);
draw2(indexOffset, indexCount, instanceCount);
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
@@ -101,10 +101,6 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh);
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
+46 -62
View File
@@ -99,6 +99,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
#endif
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
auto& bindingToName = mInfo->bindingToName;
#endif
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -109,12 +113,16 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
uint32_t const binding = samplers[i].binding;
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
bindingToName[binding] = samplers[i].name.c_str();
#endif
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
<< ", " << modules[1] << ")" << utils::io::endl;
#endif
}
@@ -281,32 +289,21 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co
return count;
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
attributes(attributes) {
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
mInfo(attributes.size()) {
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mResources(allocator) {
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo.mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo.mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
VulkanVertexBufferInfo const* const vbi = allocator->handle_cast<VulkanVertexBufferInfo*>(vbih);
mInfo = new PipelineInfo(vbi->attributes.size());
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
auto attribToBufferIndex = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
std::fill(mInfo->mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo->mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) {
Attribute attrib = vbi->attributes[attribIndex];
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute attrib = attributes[attribIndex];
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger);
@@ -318,7 +315,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
attrib = vbi->attributes[0];
attrib = attributes[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -334,18 +331,23 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
}
}
VulkanVertexBuffer::~VulkanVertexBuffer() {
delete mInfo;
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
mResources(allocator) {
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
size_t count = vbi->attributes.size();
auto vkbuffers = mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
auto attribToBuffer = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
size_t const count = vbi->getAttributeCount();
VkBuffer* const vkbuffers = getVkBuffers();
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
if (attribToBuffer[attribIndex] == static_cast<int8_t>(index)) {
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
@@ -361,35 +363,6 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
this->type = pt;
switch (pt) {
case PrimitiveType::POINTS:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
break;
case PrimitiveType::LINES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
break;
case PrimitiveType::LINE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
break;
case PrimitiveType::TRIANGLES:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
break;
case PrimitiveType::TRIANGLE_STRIP:
primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
break;
}
}
void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
VulkanIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
@@ -421,4 +394,15 @@ bool VulkanTimerQuery::isCompleted() noexcept {
VulkanTimerQuery::~VulkanTimerQuery() = default;
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(resourceAllocator) {
type = pt;
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
} // namespace filament::backend
+60 -44
View File
@@ -64,6 +64,12 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
return mInfo->bindingToName;
}
#endif
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
@@ -80,6 +86,12 @@ private:
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
// We store the sampler name mapped from binding index (only for debug purposes).
utils::FixedCapacityVector<std::string> bindingToName;
#endif
};
PipelineInfo* mInfo;
@@ -132,7 +144,48 @@ struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
AttributeArray attributes;
inline VkVertexInputAttributeDescription const* getAttribDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
}
inline VkVertexInputBindingDescription const* getBufferDescriptions() const {
return mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
}
inline int8_t const* getAttributeToBuffer() const {
return mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
}
inline VkDeviceSize const* getOffsets() const {
return mInfo.mSoa.data<PipelineInfo::OFFSETS>();
}
size_t getAttributeCount() const noexcept {
return mInfo.mSoa.size();
}
private:
struct PipelineInfo {
PipelineInfo(size_t size) : mSoa(size /* capacity */) {
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t OFFSETS = 2;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3;
utils::StructureOfArrays<
VkVertexInputAttributeDescription,
VkVertexInputBindingDescription,
VkDeviceSize,
int8_t
> mSoa;
};
PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
@@ -140,53 +193,20 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
~VulkanVertexBuffer();
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
}
inline VkVertexInputBindingDescription const* getBufferDescriptions() {
return mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
}
inline VkBuffer const* getVkBuffers() const {
return mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
return mBuffers.data();
}
inline VkDeviceSize const* getOffsets() const {
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
inline VkBuffer* getVkBuffers() {
return mBuffers.data();
}
Handle<HwVertexBufferInfo> vbih;
private:
struct PipelineInfo {
PipelineInfo(size_t size)
: mSoa(size /* capacity */) {
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t VK_BUFFER = 2;
static constexpr uint8_t OFFSETS = 3;
static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4;
utils::StructureOfArrays<
VkVertexInputAttributeDescription,
VkVertexInputBindingDescription,
VkBuffer,
VkDeviceSize,
int8_t
> mSoa;
};
PipelineInfo* mInfo;
utils::FixedCapacityVector<VkBuffer> mBuffers;
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
};
@@ -219,19 +239,15 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
VulkanRenderPrimitive(VulkanResourceAllocator* allocator)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(allocator) {}
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
~VulkanRenderPrimitive() {
mResources.clear();
}
void setPrimitiveType(PrimitiveType pt);
void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer);
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
VkPrimitiveTopology primitiveTopology;
private:
// Keep references to the vertex buffer and the index buffer.
@@ -563,6 +563,14 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
// If this is a debug build, validate the current shader.
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
}
#endif
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
@@ -191,10 +191,19 @@ public:
mDummyTargetInfo.imageView = imageView;
}
// Acquires a resource to be bound to the current pipeline. The ownership of the resource
// will be transferred to the corresponding pipeline when pipeline is bound.
void acquireResource(VulkanResource* resource) {
mPipelineBoundResources.acquire(resource);
static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept {
switch (pt) {
case PrimitiveType::POINTS:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case PrimitiveType::LINES:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case PrimitiveType::LINE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case PrimitiveType::TRIANGLES:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case PrimitiveType::TRIANGLE_STRIP:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
}
}
private:
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include <private/backend/HandleAllocator.h>
@@ -29,18 +30,17 @@
namespace filament::backend {
// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType.
#define RESOURCE_TYPE_COUNT 12
#define DEBUG_RESOURCE_LEAKS 0
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
#if DEBUG_RESOURCE_LEAKS
#define TRACK_INCREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]++; \
#define TRACK_INCREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
}
#define TRACK_DECREMENT() \
if (!IS_MANAGED_TYPE(obj->mType)) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->mType)]--; \
#define TRACK_DECREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
}
#else
// No-op
@@ -70,6 +70,7 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
case VulkanResourceType::END_TYPE:
break;
}
}
@@ -51,6 +51,8 @@ enum class VulkanResourceType : uint8_t {
// Below are resources that are managed manually (i.e. not ref counted).
FENCE,
HEAP_ALLOCATED,
END_TYPE, // A placeholder
};
#define IS_HEAP_ALLOC_TYPE(f) \
@@ -159,7 +159,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) {
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
utils::slog.d << "vkCreateImage: "
<< "image = " << mTextureImage << ", "
<< "result = " << error << ", "
+1 -1
View File
@@ -300,7 +300,7 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
* Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
+4 -2
View File
@@ -41,6 +41,7 @@
#include "details/Material.h"
#include "details/MaterialInstance.h"
#include "details/Texture.h"
#include "details/VertexBuffer.h"
#include "generated/resources/materials.h"
@@ -190,8 +191,9 @@ std::pair<backend::PipelineState, backend::Viewport>
FMaterial* const material = getMaterial(engine);
material->prepareProgram(Variant{ variantKey });
return {{
.program = material->getProgram(Variant{ variantKey }),
.rasterState = material->getRasterState() },
.program = material->getProgram(Variant{ variantKey }),
.vertexBufferInfo = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle(),
.rasterState = material->getRasterState() },
material->getDefaultInstance()->getScissor() };
}
+36 -3
View File
@@ -55,6 +55,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
using namespace utils;
using namespace filament::math;
@@ -866,6 +867,10 @@ void RenderPass::Executor::execute(FEngine& engine,
.polygonOffset = mPolygonOffset,
};
PipelineState currentPipeline{};
Handle<HwRenderPrimitive> currentPrimitiveHandle{};
bool rebindPipeline = true;
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
FMaterial const* UTILS_RESTRICT ma = nullptr;
auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data();
@@ -918,7 +923,9 @@ void RenderPass::Executor::execute(FEngine& engine,
// per-renderable uniform
PrimitiveInfo const info = first->primitive;
pipeline.rasterState = info.rasterState;
//pipeline.vertexBufferInfo = info.vertexBufferInfo;
pipeline.vertexBufferInfo = info.primitive->getVertexBufferInfoHandle();
pipeline.primitiveType = info.primitive->getPrimitiveType();
assert_invariant(pipeline.vertexBufferInfo);
if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) {
// this is always taken the first time
@@ -940,6 +947,11 @@ void RenderPass::Executor::execute(FEngine& engine,
}
pipeline.stencilState = mi->getStencilState();
mi->use(driver);
// FIXME: MaterialInstance changed (not necessarily the program though),
// however, texture bindings may have changed and currently we need to
// rebind the pipeline when that happens.
rebindPipeline = true;
}
assert_invariant(ma);
@@ -989,6 +1001,10 @@ void RenderPass::Executor::execute(FEngine& engine,
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
// FIXME: Currently we need to rebind the PipelineState when texture or
// UBO binding change.
rebindPipeline = true;
}
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
@@ -1001,10 +1017,27 @@ void RenderPass::Executor::execute(FEngine& engine,
// note: even if only morphing is enabled, binding skinningTexture is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
// FIXME: Currently we need to rebind the PipelineState when texture or
// UBO binding change.
rebindPipeline = true;
}
driver.draw(pipeline, info.primitive->getHwHandle(),
info.primitive->getIndexOffset(), info.primitive->getIndexCount(),
if (rebindPipeline ||
(memcmp(&pipeline, &currentPipeline, sizeof(PipelineState)) != 0)) {
rebindPipeline = false;
currentPipeline = pipeline;
driver.bindPipeline(pipeline);
}
if (info.primitive->getHwHandle() != currentPrimitiveHandle) {
currentPrimitiveHandle = info.primitive->getHwHandle();
driver.bindRenderPrimitive(info.primitive->getHwHandle());
}
driver.draw2(
info.primitive->getIndexOffset(),
info.primitive->getIndexCount(),
instanceCount);
}
}
+14 -19
View File
@@ -16,14 +16,18 @@
#include "RenderPrimitive.h"
#include "details/Engine.h"
#include <filament/RenderableManager.h>
#include <filament/MaterialEnums.h>
#include "details/IndexBuffer.h"
#include "details/Material.h"
#include "details/MaterialInstance.h"
#include "details/VertexBuffer.h"
#include <private/backend/CommandStream.h>
#include <backend/DriverApiForward.h>
#include <utils/debug.h>
#include <stdint.h>
#include <stddef.h>
namespace filament {
@@ -39,18 +43,7 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
if (entry.indices && entry.vertices) {
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
FIndexBuffer* indexBuffer = downcast(entry.indices);
AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
auto const& ebh = vertexBuffer->getHwHandle();
auto const& ibh = indexBuffer->getHwHandle();
mHandle = factory.create(driver, ebh, ibh, entry.type);
mPrimitiveType = entry.type;
mIndexOffset = entry.offset;
mIndexCount = entry.count;
mEnabledAttributes = enabledAttributes;
set(factory, driver, entry.type, vertexBuffer, indexBuffer, entry.offset, entry.count);
}
}
@@ -62,17 +55,19 @@ void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::Dri
void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept {
FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer,
size_t offset, size_t count) noexcept {
if (mHandle) {
factory.destroy(driver, mHandle);
}
AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes();
AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
auto const& ebh = vertices->getHwHandle();
auto const& ibh = indices->getHwHandle();
auto const& ebh = vertexBuffer->getHwHandle();
auto const& ibh = indexBuffer->getHwHandle();
mHandle = factory.create(driver, ebh, ibh, type);
mVertexBufferInfoHandle = vertexBuffer->getVertexBufferInfoHandle();
mPrimitiveType = type;
mIndexOffset = offset;
+4 -3
View File
@@ -45,14 +45,15 @@ public:
void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, size_t offset,
size_t count) noexcept;
// frees driver resources, object becomes invalid
void terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver);
const FMaterialInstance* getMaterialInstance() const noexcept { return mMaterialInstance; }
backend::Handle<backend::HwRenderPrimitive> getHwHandle() const noexcept { return mHandle; }
backend::RenderPrimitiveHandle getHwHandle() const noexcept { return mHandle; }
backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
uint32_t getIndexOffset() const noexcept { return mIndexOffset; }
uint32_t getIndexCount() const noexcept { return mIndexCount; }
@@ -76,7 +77,7 @@ private:
struct {
FMaterialInstance const* mMaterialInstance = nullptr;
backend::Handle<backend::HwRenderPrimitive> mHandle = {};
UTILS_UNUSED uint8_t padding[4]= {};
backend::Handle<backend::HwVertexBufferInfo> mVertexBufferInfoHandle = {};
uint32_t mIndexOffset = 0;
uint32_t mIndexCount = 0;
};
+2
View File
@@ -52,6 +52,8 @@ public:
VertexBufferHandle getHwHandle() const noexcept { return mHandle; }
VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
size_t getVertexCount() const noexcept;
AttributeBitset getDeclaredAttributes() const noexcept {
@@ -1,12 +0,0 @@
material {
name : "Filament Default Material",
shadingModel : unlit,
featureLevel: 0
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = vec3(0.8);
}
}
+5 -1
View File
@@ -35,10 +35,14 @@ fragment {
if (materialParams.constantColor != 0) {
sky = materialParams.color;
} else {
#if MATERIAL_FEATURE_LEVEL == 0
sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
#else
sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0);
#endif
sky.rgb *= frameUniforms.iblLuminance;
}
if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0f) {
if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) {
vec3 direction = normalize(variable_eyeDirection.xyz);
// Assume the sun is a sphere
vec3 sun = frameUniforms.lightColorIntensity.rgb *
-60
View File
@@ -1,60 +0,0 @@
material {
name : Skybox,
parameters : [
{
type : int,
name : showSun
},
{
type : int,
name : constantColor
},
{
type : samplerCubemap,
name : skybox
},
{
type : float4,
name : color
}
],
variables : [
eyeDirection
],
vertexDomain : device,
depthWrite : false,
shadingModel : unlit,
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none,
featureLevel: 0
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec4 sky;
if (materialParams.constantColor != 0) {
sky = materialParams.color;
} else {
sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
sky.rgb *= frameUniforms.iblLuminance;
}
if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) {
vec3 direction = normalize(variable_eyeDirection.xyz);
// Assume the sun is a sphere
vec3 sun = frameUniforms.lightColorIntensity.rgb *
(frameUniforms.lightColorIntensity.a * (4.0 * PI));
float cosAngle = dot(direction, frameUniforms.lightDirection);
float x = (cosAngle - frameUniforms.sun.x) * frameUniforms.sun.z;
float gradient = pow(1.0 - saturate(x), frameUniforms.sun.w);
sky.rgb = sky.rgb + gradient * sun;
}
material.baseColor = sky;
}
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.eyeDirection.xyz = material.worldPosition.xyz;
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.50.6"
spec.version = "1.51.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.6/filament-v1.50.6-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 50;
static constexpr size_t MATERIAL_VERSION = 51;
/**
* Supported shading models
@@ -201,7 +201,7 @@ enum class ReflectionMode : uint8_t {
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
using AttributeBitset = utils::bitset32;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 26;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27;
enum class Property : uint8_t {
BASE_COLOR, //!< float4, all shading models
ROUGHNESS, //!< float, lit shading models only
@@ -223,6 +223,7 @@ enum class Property : uint8_t {
EMISSIVE, //!< float4, all shading models
NORMAL, //!< float3, all shading models only, except unlit
POST_LIGHTING_COLOR, //!< float4, all shading models
POST_LIGHTING_MIX_FACTOR,//!< float, all shading models
CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only
ABSORPTION, //!< float3, how much light is absorbed by the material
TRANSMISSION, //!< float, how much light is refracted through the material
@@ -410,7 +410,8 @@ public:
/**
* Set the blending mode of the post-lighting color for this material.
* Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT.
* This setting requires the material property "postLightingColor" to be set.
* This setting requires the material properties "postLightingColor" and
* "postLightingMixFactor" to be set.
*/
MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept;
+27 -26
View File
@@ -21,32 +21,33 @@
namespace filamat {
std::unordered_map<std::string, Property> Enums::mStringToProperty = {
{ "baseColor", Property::BASE_COLOR },
{ "roughness", Property::ROUGHNESS },
{ "metallic", Property::METALLIC },
{ "reflectance", Property::REFLECTANCE },
{ "ambientOcclusion", Property::AMBIENT_OCCLUSION },
{ "clearCoat", Property::CLEAR_COAT },
{ "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS },
{ "clearCoatNormal", Property::CLEAR_COAT_NORMAL },
{ "anisotropy", Property::ANISOTROPY },
{ "anisotropyDirection", Property::ANISOTROPY_DIRECTION },
{ "thickness", Property::THICKNESS },
{ "subsurfacePower", Property::SUBSURFACE_POWER },
{ "subsurfaceColor", Property::SUBSURFACE_COLOR },
{ "sheenColor", Property::SHEEN_COLOR },
{ "sheenRoughness", Property::SHEEN_ROUGHNESS },
{ "glossiness", Property::GLOSSINESS },
{ "specularColor", Property::SPECULAR_COLOR },
{ "emissive", Property::EMISSIVE },
{ "normal", Property::NORMAL },
{ "postLightingColor", Property::POST_LIGHTING_COLOR },
{ "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM },
{ "absorption", Property::ABSORPTION },
{ "transmission", Property::TRANSMISSION },
{ "ior", Property::IOR },
{ "microThickness", Property::MICRO_THICKNESS },
{ "bentNormal", Property::BENT_NORMAL },
{ "baseColor", Property::BASE_COLOR },
{ "roughness", Property::ROUGHNESS },
{ "metallic", Property::METALLIC },
{ "reflectance", Property::REFLECTANCE },
{ "ambientOcclusion", Property::AMBIENT_OCCLUSION },
{ "clearCoat", Property::CLEAR_COAT },
{ "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS },
{ "clearCoatNormal", Property::CLEAR_COAT_NORMAL },
{ "anisotropy", Property::ANISOTROPY },
{ "anisotropyDirection", Property::ANISOTROPY_DIRECTION },
{ "thickness", Property::THICKNESS },
{ "subsurfacePower", Property::SUBSURFACE_POWER },
{ "subsurfaceColor", Property::SUBSURFACE_COLOR },
{ "sheenColor", Property::SHEEN_COLOR },
{ "sheenRoughness", Property::SHEEN_ROUGHNESS },
{ "glossiness", Property::GLOSSINESS },
{ "specularColor", Property::SPECULAR_COLOR },
{ "emissive", Property::EMISSIVE },
{ "normal", Property::NORMAL },
{ "postLightingColor", Property::POST_LIGHTING_COLOR },
{ "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR },
{ "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM },
{ "absorption", Property::ABSORPTION },
{ "transmission", Property::TRANSMISSION },
{ "ior", Property::IOR },
{ "microThickness", Property::MICRO_THICKNESS },
{ "bentNormal", Property::BENT_NORMAL },
};
template <>
+27 -26
View File
@@ -1115,32 +1115,33 @@ io::sstream& CodeGenerator::generateShaderReflections(utils::io::sstream& out, S
char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) noexcept {
using Property = MaterialBuilder::Property;
switch (property) {
case Property::BASE_COLOR: return "BASE_COLOR";
case Property::ROUGHNESS: return "ROUGHNESS";
case Property::METALLIC: return "METALLIC";
case Property::REFLECTANCE: return "REFLECTANCE";
case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION";
case Property::CLEAR_COAT: return "CLEAR_COAT";
case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS";
case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL";
case Property::ANISOTROPY: return "ANISOTROPY";
case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION";
case Property::THICKNESS: return "THICKNESS";
case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER";
case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR";
case Property::SHEEN_COLOR: return "SHEEN_COLOR";
case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS";
case Property::GLOSSINESS: return "GLOSSINESS";
case Property::SPECULAR_COLOR: return "SPECULAR_COLOR";
case Property::EMISSIVE: return "EMISSIVE";
case Property::NORMAL: return "NORMAL";
case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR";
case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM";
case Property::ABSORPTION: return "ABSORPTION";
case Property::TRANSMISSION: return "TRANSMISSION";
case Property::IOR: return "IOR";
case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
case Property::BENT_NORMAL: return "BENT_NORMAL";
case Property::BASE_COLOR: return "BASE_COLOR";
case Property::ROUGHNESS: return "ROUGHNESS";
case Property::METALLIC: return "METALLIC";
case Property::REFLECTANCE: return "REFLECTANCE";
case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION";
case Property::CLEAR_COAT: return "CLEAR_COAT";
case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS";
case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL";
case Property::ANISOTROPY: return "ANISOTROPY";
case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION";
case Property::THICKNESS: return "THICKNESS";
case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER";
case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR";
case Property::SHEEN_COLOR: return "SHEEN_COLOR";
case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS";
case Property::GLOSSINESS: return "GLOSSINESS";
case Property::SPECULAR_COLOR: return "SPECULAR_COLOR";
case Property::EMISSIVE: return "EMISSIVE";
case Property::NORMAL: return "NORMAL";
case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR";
case Property::POST_LIGHTING_MIX_FACTOR: return "POST_LIGHTING_MIX_FACTOR";
case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM";
case Property::ABSORPTION: return "ABSORPTION";
case Property::TRANSMISSION: return "TRANSMISSION";
case Property::IOR: return "IOR";
case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
case Property::BENT_NORMAL: return "BENT_NORMAL";
}
}
+2
View File
@@ -701,6 +701,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.postLightingColor = vec4(1.0);
material.postLightingMixFactor = 0.5;
}
)");
@@ -712,6 +713,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties);
MaterialBuilder::PropertyList expected{ false };
expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_COLOR)] = true;
expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_MIX_FACTOR)] = true;
EXPECT_TRUE(PropertyListsMatch(expected, properties));
}
@@ -88,12 +88,23 @@ public:
*/
class EquirectangularToCubemap {
public:
struct Config {
bool mirror = true; //!< mirror the source horizontally
};
/**
* Creates a EquirectangularToCubemap processor.
* Creates a EquirectangularToCubemap processor using the default Config
* @param context IBLPrefilterContext to use
*/
explicit EquirectangularToCubemap(IBLPrefilterContext& context);
/**
* Creates a EquirectangularToCubemap processor using the provided Config
* @param context IBLPrefilterContext to use
*/
EquirectangularToCubemap(IBLPrefilterContext& context, Config const& config);
/**
* Destroys all GPU resources created during initialization.
*/
@@ -125,6 +136,7 @@ public:
private:
IBLPrefilterContext& mContext;
filament::Material* mEquirectMaterial = nullptr;
Config mConfig{};
};
/**
+23 -4
View File
@@ -19,6 +19,7 @@
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/MaterialEnums.h>
#include <filament/RenderTarget.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
@@ -29,12 +30,22 @@
#include <filament/View.h>
#include <filament/Viewport.h>
#include <utils/Panic.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/EntityManager.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <math/scalar.h>
#include <math/vec4.h>
#include <algorithm>
#include <cmath>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include "generated/resources/iblprefilter_materials.h"
@@ -167,13 +178,19 @@ IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) n
// ------------------------------------------------------------------------------------------------
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
IBLPrefilterContext& context) : mContext(context) {
IBLPrefilterContext& context,
IBLPrefilterContext::EquirectangularToCubemap::Config const& config)
: mContext(context), mConfig(config) {
Engine& engine = mContext.mEngine;
mEquirectMaterial = Material::Builder().package(
IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_DATA,
IBLPREFILTER_MATERIALS_EQUIRECTTOCUBE_SIZE).build(engine);
}
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
IBLPrefilterContext& context) : EquirectangularToCubemap(context, {}) {
}
IBLPrefilterContext::EquirectangularToCubemap::~EquirectangularToCubemap() noexcept {
Engine& engine = mContext.mEngine;
engine.destroy(mEquirectMaterial);
@@ -257,6 +274,8 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()(
.texture(RenderTarget::AttachmentPoint::COLOR1, outCube)
.texture(RenderTarget::AttachmentPoint::COLOR2, outCube);
mi->setParameter("mirror", mConfig.mirror ? -1.0f : 1.0f);
for (size_t i = 0; i < 2; i++) {
mi->setParameter("side", i == 0 ? 1.0f : -1.0f);
@@ -10,6 +10,11 @@ material {
type : float,
name : side,
precision: medium
},
{
type : float,
name : mirror,
precision: medium
}
],
outputs : [
@@ -51,7 +56,7 @@ highp vec2 toEquirect(const highp vec3 s) {
highp float xf = atan(s.x, s.z) * (1.0 / PI); // range [-1.0, 1.0]
highp float yf = asin(s.y) * (2.0 / PI); // range [-1.0, 1.0]
xf = (xf + 1.0) * 0.5; // range [0, 1.0]
yf = (1.0 - yf) * 0.5; // range [0, 1.0]
yf = (yf + 1.0) * 0.5; // range [0, 1.0]
return vec2(xf, yf);
}
@@ -62,18 +67,16 @@ mediump vec3 sampleEquirect(mediump sampler2D equirect, const highp vec3 r) {
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy; // interpolated at pixel's center
highp vec2 p = vec2(
uv.x * 2.0 - 1.0,
1.0 - uv.y * 2.0);
highp vec2 p = uv * 2.0 - 1.0;
float side = materialParams.side;
float mirror = materialParams.mirror;
highp float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0);
// compute the view (and normal, since v = n) direction for each face
highp vec3 rx = vec3( side, p.y, side * -p.x);
highp vec3 ry = vec3( p.x, side, side * -p.y);
highp vec3 rz = vec3(side * p.x, p.y, side);
highp vec3 rx = vec3( side * mirror, p.y, side * -p.x);
highp vec3 ry = vec3( p.x * mirror, side, side * -p.y);
highp vec3 rz = vec3(side * p.x * mirror, p.y, side);
postProcess.outx = sampleEquirect(materialParams_equirect, rx * l);
postProcess.outy = sampleEquirect(materialParams_equirect, ry * l);
@@ -122,9 +122,10 @@ void postProcess(inout PostProcessInputs postProcess) {
float side = materialParams.side;
// compute the view (and normal, since v = n) direction for each face
vec3 rx = normalize(vec3( side, -p.y, side * -p.x));
vec3 ry = normalize(vec3( p.x, side, side * p.y));
vec3 rz = normalize(vec3(side * p.x, -p.y, side));
float l = inversesqrt(p.x * p.x + p.y * p.y + 1.0);
vec3 rx = vec3( side, p.y, side * -p.x) * l;
vec3 ry = vec3( p.x, side, side * -p.y) * l;
vec3 rz = vec3(side * p.x, p.y, side) * l;
// random rotation around r
mediump float a = 2.0 * PI * random(gl_FragCoord.xy);
+6
View File
@@ -222,6 +222,12 @@ function(add_demo NAME)
target_link_libraries(${NAME} PRIVATE sample-resources filamentapp)
target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS})
set_target_properties(${NAME} PROPERTIES FOLDER Samples)
# This is needed after XCode 15.3
if (APPLE)
set_target_properties(${NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
set_target_properties(${NAME} PROPERTIES INSTALL_RPATH /usr/local/lib)
endif()
endfunction()
if (NOT ANDROID)
+7 -5
View File
@@ -6,17 +6,19 @@ layout(location = 0) out vec4 fragColor;
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
void blendPostLightingColor(const MaterialInputs material, inout vec4 color) {
vec4 blend = color;
#if defined(POST_LIGHTING_BLEND_MODE_OPAQUE)
color = material.postLightingColor;
blend = material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_TRANSPARENT)
color = material.postLightingColor + color * (1.0 - material.postLightingColor.a);
blend = material.postLightingColor + color * (1.0 - material.postLightingColor.a);
#elif defined(POST_LIGHTING_BLEND_MODE_ADD)
color += material.postLightingColor;
blend += material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_MULTIPLY)
color *= material.postLightingColor;
blend *= material.postLightingColor;
#elif defined(POST_LIGHTING_BLEND_MODE_SCREEN)
color += material.postLightingColor * (1.0 - color);
blend += material.postLightingColor * (1.0 - color);
#endif
color = mix(color, blend, material.postLightingMixFactor);
}
#endif
+2
View File
@@ -66,6 +66,7 @@ struct MaterialInputs {
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
vec4 postLightingColor;
float postLightingMixFactor;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
@@ -153,6 +154,7 @@ void initMaterial(out MaterialInputs material) {
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
material.postLightingColor = vec4(0.0);
material.postLightingMixFactor = 1.0;
#endif
#if !defined(SHADING_MODEL_CLOTH) && !defined(SHADING_MODEL_SUBSURFACE) && !defined(SHADING_MODEL_UNLIT)
@@ -1283,6 +1283,11 @@ bool ParametersProcessor::process(filamat::MaterialBuilder& builder, const std::
case JsonishValue::Type::STRING:
var = std::make_unique<JsonishString>(value);
break;
default:
std::cerr << "Unsupported type: \""
<< JsonishValue::typeToString(mParameters.at(key).rootAssert)
<< "\"" << std::endl;
return false;
}
auto fPointer = mParameters[key].callback;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.50.6",
"version": "1.51.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",