Compare commits

..

1 Commits

Author SHA1 Message Date
Powei Feng
b7244464a3 test: fix VK backend FeedbackLoops test 2024-02-22 11:34:03 -08:00
139 changed files with 632 additions and 662 deletions

View File

@@ -743,8 +743,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
add_subdirectory(${LIBRARIES}/bluevk)
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
add_subdirectory(${EXTERNAL}/spirv-headers)
endif()
set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)

View File

@@ -7,4 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.49.0'
implementation 'com.google.android.filament:filament-android:1.47.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.49.0'
pod 'Filament', '~> 1.47.0'
```
### Snapshots

View File

@@ -7,11 +7,6 @@ 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.49.0
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
- matc: Generate skinning variants for FL0 materials [⚠️ **Recompile materials**]
## v1.48.0
- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials

View File

@@ -38,7 +38,7 @@ Java_com_google_android_filament_EntityManager_nCreateArray(JNIEnv* env, jclass,
// (which it is), but still.
em->create((size_t) n, reinterpret_cast<Entity *>(entities));
env->ReleaseIntArrayElements(entities_, entities, JNI_ABORT);
env->ReleaseIntArrayElements(entities_, entities, 0);
}
extern "C" JNIEXPORT jint JNICALL

View File

@@ -205,7 +205,7 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
break;
}
env->ReleaseIntArrayElements(v_, v, JNI_ABORT);
env->ReleaseIntArrayElements(v_, v, 0);
env->ReleaseStringUTFChars(name_, name);
}

View File

@@ -114,6 +114,6 @@ Java_com_google_android_filament_Scene_nGetEntities(JNIEnv *env, jclass ,
out[i++] = (jint) entity.getId();
}
});
env->ReleaseIntArrayElements(outArray, (jint*) out, 0);
env->ReleaseIntArrayElements(outArray, (jint*) out, JNI_ABORT);
return JNI_TRUE;
}

View File

@@ -182,85 +182,28 @@ public class UiHelper {
void detach();
}
private class SurfaceViewHandler implements RenderSurface, SurfaceHolder.Callback {
@NonNull private final SurfaceView mSurfaceView;
private static class SurfaceViewHandler implements RenderSurface {
private final SurfaceView mSurfaceView;
SurfaceViewHandler(@NonNull SurfaceView surfaceView) {
mSurfaceView = surfaceView;
@NonNull SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceViewHandler(SurfaceView surface) {
mSurfaceView = surface;
}
@Override
public void resize(int width, int height) {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.setFixedSize(width, height);
mSurfaceView.getHolder().setFixedSize(width, height);
}
@Override
public void detach() {
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
holder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class SurfaceHolderHandler implements RenderSurface, SurfaceHolder.Callback {
private static class SurfaceHolderHandler implements RenderSurface {
private final SurfaceHolder mSurfaceHolder;
SurfaceHolderHandler(@NonNull SurfaceHolder holder) {
mSurfaceHolder = holder;
holder.addCallback(this);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
surfaceCreated(holder);
// there is no way to retrieve the actual PixelFormat, since it is not used
// in the callback, we can use whatever we want.
surfaceChanged(holder, PixelFormat.RGBA_8888,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
SurfaceHolderHandler(SurfaceHolder surface) {
mSurfaceHolder = surface;
}
@Override
@@ -270,55 +213,19 @@ public class UiHelper {
@Override
public void detach() {
mSurfaceHolder.removeCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
}
private class TextureViewHandler implements RenderSurface, TextureView.SurfaceTextureListener {
private class TextureViewHandler implements RenderSurface {
private final TextureView mTextureView;
private Surface mSurface;
TextureViewHandler(@NonNull TextureView view) {
mTextureView = view;
mTextureView.setSurfaceTextureListener(this);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
if (surfaceTexture != null) {
this.onSurfaceTextureAvailable(surfaceTexture,
mDesiredWidth, mDesiredHeight);
}
}
}
TextureViewHandler(TextureView surface) { mTextureView = surface; }
@Override
public void resize(int width, int height) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture != null) {
surfaceTexture.setDefaultBufferSize(width, height);
}
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
}
if (mRenderCallback != null) {
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
@@ -328,69 +235,10 @@ public class UiHelper {
@Override
public void detach() {
mTextureView.setSurfaceTextureListener(null);
setSurface(null);
}
@Override
public void onSurfaceTextureAvailable(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
final Surface surface = new Surface(surfaceTexture);
setSurface(surface);
createSwapChain(surface);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mRenderCallback != null) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
final Surface surface = getSurface();
if (surface != null) {
mRenderCallback.onNativeWindowChanged(surface);
}
}
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
private void setSurface(@Nullable Surface surface) {
void setSurface(Surface surface) {
if (surface == null) {
if (mSurface != null) {
mSurface.release();
@@ -399,7 +247,7 @@ public class UiHelper {
mSurface = surface;
}
private Surface getSurface() {
public Surface getSurface() {
return mSurface;
}
}
@@ -554,8 +402,48 @@ public class UiHelper {
view.setZOrderOnTop(translucent);
}
view.getHolder().setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
view.getHolder().setFormat(format);
mRenderSurface = new SurfaceViewHandler(view);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
SurfaceHolder holder = view.getHolder();
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceView's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}
@@ -567,7 +455,76 @@ public class UiHelper {
public void attachTo(@NonNull TextureView view) {
if (attach(view)) {
view.setOpaque(isOpaque());
mRenderSurface = new TextureViewHandler(view);
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
}
}
Surface surface = new Surface(surfaceTexture);
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
textureViewHandler.setSurface(surface);
createSwapChain(surface);
if (mRenderCallback != null) {
// Call this the first time because onSurfaceTextureSizeChanged()
// isn't called at initialization time
mRenderCallback.onResized(width, height);
}
}
@Override
public void onSurfaceTextureSizeChanged(
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
if (mRenderCallback != null) {
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
} else {
mRenderCallback.onResized(width, height);
}
// We must recreate the SwapChain to guarantee that it sees the new size.
// More precisely, for an EGL client, the EGLSurface must be recreated. For
// a Vulkan client, the SwapChain must be recreated. Calling
// onNativeWindowChanged() will accomplish that.
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
// documentation.
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
mRenderCallback.onNativeWindowChanged(textureViewHandler.getSurface());
}
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
destroySwapChain();
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
};
view.setSurfaceTextureListener(listener);
// in case the View's SurfaceTexture already existed
if (view.isAvailable()) {
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
if (surfaceTexture != null) {
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
}
}
}
}
@@ -578,8 +535,46 @@ public class UiHelper {
*/
public void attachTo(@NonNull SurfaceHolder holder) {
if (attach(holder)) {
holder.setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
holder.setFormat(format);
mRenderSurface = new SurfaceHolderHandler(holder);
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
createSwapChain(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// Note: this is always called at least once after surfaceCreated()
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
if (mRenderCallback != null) {
mRenderCallback.onResized(width, height);
}
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
destroySwapChain();
}
};
holder.addCallback(callback);
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
}
// in case the SurfaceHolder's surface already existed
final Surface surface = holder.getSurface();
if (surface != null && surface.isValid()) {
callback.surfaceCreated(holder);
callback.surfaceChanged(holder, format,
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
}
}
}

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.49.0
VERSION_NAME=1.47.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -50,7 +50,7 @@ function replace {
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
local FILE_NAME="$2"
if [ $IS_DARWIN == 1 ]; then
if [ IS_DARWIN ]; then
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
else
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"

View File

@@ -169,8 +169,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/spirv/VulkanSpirvUtils.cpp
src/vulkan/spirv/VulkanSpirvUtils.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
@@ -315,7 +313,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
endif()
if (FILAMENT_SUPPORTS_METAL)

View File

@@ -1180,8 +1180,8 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
enum class Workaround : uint16_t {
// The EASU pass must split because shader compiler flattens early-exit branch
SPLIT_EASU,
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're
// read-only for the whole render pass.
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for
// the whole render pass.
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
ADRENO_UNIFORM_ARRAY_CRASH,

View File

@@ -262,12 +262,7 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
mContext.bindTexture(unit, t->gl.target, t->gl.id, t->gl.targetIndex);
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
if (UTILS_UNLIKELY(!p->isValid())) {
// If the program is not valid, we can't call use().
return false;
}
void OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
// set-up textures and samplers in the proper TMUs (as specified in setSamplers)
p->use(this, mContext);
@@ -282,7 +277,6 @@ bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
// when mPlatform.isSRGBSwapChainSupported() is false (no need to check though).
p->setRec709ColorSpace(mRec709OutputColorspace);
}
return true;
}
@@ -3537,17 +3531,18 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
DEBUG_MARKER()
auto& gl = mContext;
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(state.program);
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.
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
GLRenderPrimitive* const rp = handle_cast<GLRenderPrimitive*>(rph);
useProgram(p);
GLRenderPrimitive* rp = handle_cast<GLRenderPrimitive *>(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
@@ -3558,7 +3553,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
gl.bindVertexArray(&rp->gl);
// If necessary, mutate the bindings in the VAO.
GLVertexBuffer const* const glvb = handle_cast<GLVertexBuffer*>(vb);
const GLVertexBuffer* glvb = handle_cast<GLVertexBuffer*>(vb);
if (UTILS_UNLIKELY(rp->gl.vertexBufferVersion != glvb->bufferObjectsVersion)) {
updateVertexArrayObject(rp, glvb);
}
@@ -3592,16 +3587,17 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(program);
OpenGLProgram* p = handle_cast<OpenGLProgram*>(program);
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.
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
useProgram(p);
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
#if defined(__ANDROID__)

View File

@@ -318,7 +318,7 @@ private:
void bindTexture(GLuint unit, GLTexture const* t) noexcept;
void bindSampler(GLuint unit, GLuint sampler) noexcept;
inline bool useProgram(OpenGLProgram* p) noexcept;
inline void useProgram(OpenGLProgram* p) noexcept;
enum class ResolveAction { LOAD, STORE };
void resolvePass(ResolveAction action, GLRenderTarget const* rt,

View File

@@ -129,7 +129,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
#endif
{
// ES2 initialization of (fake) UBOs
UniformsRecord* const uniformsRecords = new(std::nothrow) UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UniformsRecord* const uniformsRecords = new UniformsRecord[Program::UNIFORM_BINDING_COUNT];
UTILS_NOUNROLL
for (GLuint binding = 0, n = Program::UNIFORM_BINDING_COUNT; binding < n; binding++) {
Program::UniformInfo& uniforms = lazyInitializationData.bindingUniformInfo[binding];

View File

@@ -59,7 +59,7 @@ public:
// - the content of any bound sampler buffer has changed
// ... since last time we used this program
// Turns out the former might be relatively cheap to check, the latter requires
// turns out the former might be relatively cheap to check, the later requires
// a bit less. Compared to what updateSamplers() actually does, which is
// pretty little, I'm not sure if we'll get ahead.

View File

@@ -22,6 +22,7 @@
#include <private/backend/BackendUtils.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/Program.h>
#include <utils/compiler.h>
@@ -253,6 +254,8 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);
programData.program = glProgram;
token->gl.program = programData.program;
// we don't need to check for success here, it'll be done on the
// main thread side.
token->set(programData);
@@ -334,12 +337,12 @@ GLuint ShaderCompilerService::getProgram(ShaderCompilerService::program_token_t&
return program;
}
void ShaderCompilerService::terminate(program_token_t& token) {
/* static*/ void ShaderCompilerService::terminate(program_token_t& token) {
assert_invariant(token);
token->canceled = true;
bool const canceled = token->compiler.cancelTickOp(token);
bool canceled = token->compiler.cancelTickOp(token);
if (token->compiler.mShaderCompilerThreadCount) {
auto job = token->compiler.mCompilerThreadPool.dequeue(token);

View File

@@ -54,8 +54,7 @@ VmaAllocator createAllocator(VkInstance instance, VkPhysicalDevice physicalDevic
VmaAllocator allocator;
VmaVulkanFunctions const funcs {
#if VMA_DYNAMIC_VULKAN_FUNCTIONS
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
.vkGetInstanceProcAddr = vkGetInstanceProcAddr, .vkGetDeviceProcAddr = vkGetDeviceProcAddr,
#else
.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties,
.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties,
@@ -427,8 +426,7 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
auto vkprogram
= mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
auto vkprogram = mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
mResourceManager.acquire(vkprogram);
}

View File

@@ -18,11 +18,10 @@
#include "VulkanConstants.h"
#include "VulkanMemory.h"
#include "spirv/VulkanSpirvUtils.h"
#include <backend/platforms/VulkanPlatform.h>
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/Panic.h>
using namespace bluevk;
@@ -47,39 +46,61 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig
rect->extent.height = std::max(top - y, 0);
}
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new PipelineInfo()),
mInfo(new PipelineInfo(builder.getSpecializationConstants().size())),
mDevice(device) {
Program::ShaderSource const& blobs = builder.getShadersSource();
auto& blobs = builder.getShadersSource();
auto& modules = mInfo->shaders;
auto const& specializationConstants = builder.getSpecializationConstants();
std::vector<uint32_t> shader;
for (size_t i = 0; i < MAX_SHADER_MODULES; i++) {
Program::ShaderBlob const& blob = blobs[i];
uint32_t* data = (uint32_t*) blob.data();
size_t dataSize = blob.size();
if (!specializationConstants.empty()) {
workaroundSpecConstant(blob, specializationConstants, shader);
data = (uint32_t*) shader.data();
dataSize = shader.size() * 4;
}
const auto& blob = blobs[i];
uint32_t* data = (uint32_t*)blob.data();
VkShaderModule& module = modules[i];
VkShaderModuleCreateInfo moduleInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = dataSize,
.codeSize = blob.size(),
.pCode = data,
};
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
}
// Note that bools are 4-bytes in Vulkan
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
constexpr uint32_t const CONSTANT_SIZE = 4;
// populate the specialization constants requirements right now
auto const& specializationConstants = builder.getSpecializationConstants();
uint32_t const specConstCount = static_cast<uint32_t>(specializationConstants.size());
char* specData = mInfo->specConstData.get();
if (specConstCount > 0) {
mInfo->specializationInfo = {
.mapEntryCount = specConstCount,
.pMapEntries = mInfo->specConsts.data(),
.dataSize = specConstCount * CONSTANT_SIZE,
.pData = specData,
};
}
for (uint32_t i = 0; i < specConstCount; ++i) {
uint32_t const offset = i * CONSTANT_SIZE;
mInfo->specConsts[i] = {
.constantID = specializationConstants[i].id,
.offset = offset,
.size = CONSTANT_SIZE,
};
using SpecConstant = Program::SpecializationConstant::Type;
char const* addr = (char*)specData + offset;
SpecConstant const& arg = specializationConstants[i].value;
if (std::holds_alternative<bool>(arg)) {
*((VkBool32*)addr) = std::get<bool>(arg) ? VK_TRUE : VK_FALSE;
} else if (std::holds_alternative<float>(arg)) {
*((float*)addr) = std::get<float>(arg);
} else {
*((int32_t*)addr) = std::get<int32_t>(arg);
}
}
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -102,7 +123,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
CustomSamplerInfoList const& samplerInfo) noexcept
: VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new PipelineInfo()),
mInfo(new PipelineInfo(0)),
mDevice(device) {
mInfo->shaders[0] = vs;
mInfo->shaders[1] = fs;

View File

@@ -25,6 +25,7 @@
#include "VulkanResources.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "private/backend/SamplerGroup.h"
@@ -37,7 +38,7 @@ class VulkanTimestamps;
struct VulkanProgram : public HwProgram, VulkanResource {
VulkanProgram(VkDevice device, Program const& builder) noexcept;
VulkanProgram(VkDevice device, const Program& builder) noexcept;
struct CustomSamplerInfo {
uint8_t groupIndex;
@@ -64,14 +65,21 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
inline VkSpecializationInfo const& getSpecConstInfo() const {
return mInfo->specializationInfo;
}
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
static constexpr uint8_t MAX_SHADER_MODULES = 2;
struct PipelineInfo {
PipelineInfo()
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
PipelineInfo(size_t specConstsCount) :
bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
specConsts(specConstsCount, VkSpecializationMapEntry{}),
specConstData(new char[specConstsCount * 4])
{}
// This bitset maps to each of the sampler in the sampler groups associated with this
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
@@ -79,7 +87,10 @@ 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 };
VkShaderModule shaders[MAX_SHADER_MODULES] = {VK_NULL_HANDLE};
VkSpecializationInfo specializationInfo = {};
utils::FixedCapacityVector<VkSpecializationMapEntry> specConsts;
std::unique_ptr<char[]> specConstData;
};
PipelineInfo* mInfo;

View File

@@ -360,7 +360,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
// If we reach this point, we need to create and stash a brand new pipeline object.
shaderStages[0].module = mPipelineRequirements.shaders[0];
shaderStages[0].pSpecializationInfo = mSpecializationRequirements;
shaderStages[1].module = mPipelineRequirements.shaders[1];
shaderStages[1].pSpecializationInfo = mSpecializationRequirements;
// Expand our size-optimized structs into the proper Vk structs.
uint32_t numVertexAttribs = 0;
@@ -566,6 +568,7 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
mSpecializationRequirements = &program->getSpecConstInfo();
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {

View File

@@ -416,6 +416,7 @@ private:
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
VkSpecializationInfo const* mSpecializationRequirements = nullptr;
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};

View File

@@ -1,148 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanSpirvUtils.h"
#include <utils/compiler.h> // UTILS_UNUSED_IN_RELEASE, UTILS_FALLTHROUGH
#include <utils/debug.h> // assert_invariant
#include <utils/FixedCapacityVector.h>
#include <spirv/unified1/spirv.hpp>
#include <unordered_map>
#include <variant>
namespace filament::backend {
namespace {
// This function transforms an OpSpecConstant instruction into just a OpConstant instruction.
// Additionally, it will adjust the value of the constant as given by the program.
void getTransformedConstantInst(SpecConstantValue* value, uint32_t* inst) {
// The first word is the size of the instruction and the instruction type.
// The second word is the type of the instruction.
// The third word is the "result id" (i.e. the variable to store into).
// The fourth word (only for floats and ints) is the literal representing the value.
// We only want to change the first and fourth words.
constexpr size_t const OP = 0;
constexpr size_t const VALUE = 3;
if (!value) {
// If a specialization wasn't provided, just switch the opcode.
uint32_t const op = inst[OP] & 0x0000FFFF;
if (op == spv::Op::OpSpecConstantFalse) {
inst[OP] = (3 << 16) | spv::Op::OpConstantFalse;
} else if (op == spv::Op::OpSpecConstantTrue) {
inst[OP] = (3 << 16) | spv::Op::OpConstantTrue;
} else if (op == spv::Op::OpSpecConstant) {
inst[OP] = (4 << 16) | spv::Op::OpConstant;
}
} else if (std::holds_alternative<bool>(*value)) {
inst[OP] = (3 << 16)
| (std::get<bool>(*value) ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse);
} else if (std::holds_alternative<float>(*value)) {
float const fval = std::get<float>(*value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&fval);
} else {
int const ival = std::get<int>(*value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&ival);
}
}
} // anonymous namespace
void workaroundSpecConstant(Program::ShaderBlob const& blob,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::vector<uint32_t>& output) {
using WordMap = std::unordered_map<uint32_t, uint32_t>;
using SpecValueMap = std::unordered_map<uint32_t, SpecConstantValue>;
constexpr size_t const HEADER_SIZE = 5;
WordMap varToIdMap;
SpecValueMap idToValue;
for (auto spec: specConstants) {
idToValue[spec.id] = spec.value;
}
// The follow loop will iterate through all the instructions and look for instructions of the
// form:
//
// OpDecorate %1 SpecId 0
// %1 = OpSpecConstantFalse %bool
//
// We want to track the mapping between the variable id %1 and the specId 0. And when seeing a
// %1 as the result, we want to change that instruction to just an OpConsant where the constant
// value is adjusted by the spec values provided in the program.
// Additionally, we will turn the SpecId decorations to no-ops.
size_t const dataSize = blob.size() / 4;
output.resize(dataSize);
uint32_t const* data = (uint32_t*) blob.data();
uint32_t* outputData = output.data();
std::memcpy(&outputData[0], &data[0], HEADER_SIZE * 4);
size_t outputCursor = HEADER_SIZE;
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
switch(op) {
case spv::Op::OpSpecConstant:
case spv::Op::OpSpecConstantTrue:
case spv::Op::OpSpecConstantFalse: {
uint32_t const targetVar = data[cursor + 2];
UTILS_UNUSED_IN_RELEASE WordMap::const_iterator idItr = varToIdMap.find(targetVar);
assert_invariant(idItr != varToIdMap.end() &&
"Cannot find variable previously decorated with SpecId");
auto valItr = idToValue.find(idItr->second);
SpecConstantValue* val = (valItr != idToValue.end()) ? &valItr->second : nullptr;
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
getTransformedConstantInst(val, &outputData[outputCursor]);
outputCursor += wordCount;
break;
}
case spv::Op::OpDecorate: {
if (data[cursor + 2] == spv::Decoration::DecorationSpecId) {
uint32_t const targetVar = data[cursor + 1];
uint32_t const specId = data[cursor + 3];
varToIdMap[targetVar] = specId;
// Note these decorations do not need to be written to the output.
break;
}
// else fallthrough and copy like all other instructions
UTILS_FALLTHROUGH;
}
default:
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
outputCursor += wordCount;
break;
}
cursor += wordCount;
}
output.resize(outputCursor);
}
} // namespace filament::backend

View File

@@ -1,47 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
#define TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
#include "backend/Program.h"
#include <utils/FixedCapacityVector.h>
#include <vector>
namespace filament::backend {
using SpecConstantValue = Program::SpecializationConstant::Type;
// For certain drivers, using spec constant can lead to compile errors [1] or undesirable behaviors
// [2]. In those instances, we simply change the spirv and set them to constants.
//
// (Implemenation note: we cannot write to the blob because spirv-validator does not properly handle
// the Nop (no-op) instruction, and swiftshader validates the shader before compilation. So we need
// to skip those instructions instead).
//
// [1]: QC driver cannot use spec constant to size fields
// (https://github.com/google/filament/issues/6444).
// [2]: An internal driver does not DCE a block guarded by a spec-const boolean set to false
// (b/310603393).
void workaroundSpecConstant(Program::ShaderBlob const& blob,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::vector<uint32_t>& output);
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H

View File

@@ -18,11 +18,16 @@
#include <private/backend/PlatformFactory.h>
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#include <backend/platforms/VulkanPlatform.h>
#endif
#include "BackendTest.h"
#include <utils/Hash.h>
#include <fstream>
#include <string>
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -33,6 +38,25 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND
using namespace filament;
using namespace filament::backend;
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
namespace filament {
class InternalVulkanPlatform : public VulkanPlatform {
public:
InternalVulkanPlatform() {
mCustomization = {
.gpu = { .index = 0 },
};
}
virtual VulkanPlatform::Customization getCustomization() const noexcept override {
return mCustomization;
}
private:
VulkanPlatform::Customization mCustomization;
};
}
#endif
#ifndef IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
@@ -40,6 +64,11 @@ using namespace filament::backend;
using namespace image;
#endif
namespace {
std::string const SHADER_VULKAN_TAG = "// select(vulkan) ";
std::string const SHADER_METAL_TAG = "// select(metal) ";
}
namespace test {
Backend BackendTest::sBackend = Backend::NOOP;
@@ -67,7 +96,14 @@ BackendTest::~BackendTest() {
void BackendTest::initializeDriver() {
auto backend = static_cast<filament::backend::Backend>(sBackend);
Platform* platform = PlatformFactory::create(&backend);
Platform* platform = nullptr;
if (backend == filament::backend::Backend::VULKAN) {
platform = new InternalVulkanPlatform();
} else {
platform = PlatformFactory::create(&backend);
}
assert_invariant(static_cast<uint8_t>(backend) == static_cast<uint8_t>(sBackend));
Platform::DriverConfig const driverConfig;
driver = platform->createDriver(nullptr, driverConfig);
@@ -193,6 +229,32 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
}
std::string BackendTest::transformShaderText(std::string const& shader) const {
std::string replacement;
switch (sBackend) {
case test::Backend::VULKAN:
replacement = SHADER_VULKAN_TAG;
break;
case test::Backend::METAL:
replacement = SHADER_METAL_TAG;
break;
default:
break;
}
if (replacement.size() == 0) {
return shader;
}
std::string replaced = shader;
while (true) {
size_t pos = replaced.find(replacement);
if (pos == std::string::npos) {
break;
}
replaced.replace(pos, replacement.length(), "");
}
return replaced;
}
class Environment : public ::testing::Environment {
public:
virtual void SetUp() override {

View File

@@ -66,6 +66,8 @@ protected:
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
std::string transformShaderText(std::string const& shader) const;
private:
filament::backend::Driver* driver = nullptr;

View File

@@ -33,6 +33,8 @@
using namespace image;
#endif
#define DUMP_INTERMEDIATE_SCREENSHOTS 1
////////////////////////////////////////////////////////////////////////////////////////////////////
// Shaders
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -42,6 +44,7 @@ layout(location = 0) in vec4 mesh_position;
void main() {
// Hack: move and scale triangle so that it covers entire viewport.
gl_Position = vec4((mesh_position.xy + 0.5) * 5.0, 0.0, 1.0);
// select(vulkan) gl_Position.y = - gl_Position.y;
})";
static std::string fullscreenFs = R"(#version 450 core
@@ -50,7 +53,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform sampler2D test_tex;
layout(binding = 0, set = 1) uniform sampler2D test_tex;
uniform Params {
highp float fbWidth;
@@ -60,7 +63,18 @@ uniform Params {
} params;
void main() {
vec2 fbsize = vec2(params.fbWidth, params.fbHeight);
vec2 uv = (gl_FragCoord.xy + 0.5) / fbsize;
float offset = 0;
// select(vulkan) offset = -offset;
vec2 uv = (gl_FragCoord.xy + offset) / fbsize;
// select(vulkan) uv.y = 1.0 - uv.y;
vec4 oo = vec4(0, 0, 0, 0);
if (uv.x > .5) {
oo += vec4(0.5, 0.0, 0.0, 1.0);
}
if (uv.y > .5) {
oo += vec4(0.0, 0.5, 0.0, 1.0);
}
// fragColor = oo;
fragColor = textureLod(test_tex, uv, params.sourceLevel);
})";
@@ -99,23 +113,58 @@ static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, Material
dapi.updateBufferObject(ubh, std::move(bd), 0);
}
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
const size_t size = kTexWidth * kTexHeight * 4;
static void readScreenshot(DriverApi& dapi, Handle<HwRenderTarget> renderTargets[kNumLevels],
uint32_t target, std::function<void(void*)> output) {
Handle<HwRenderTarget> rt = renderTargets[target];
size_t const readWidth = kTexWidth >> target;
size_t const readHeight = kTexHeight >> target;
size_t const size = readWidth * readHeight * 4;
void* buffer = calloc(1, size);
auto cb = [](void* buffer, size_t size, void* user) {
int w = kTexWidth, h = kTexHeight;
const uint32_t* texels = (uint32_t*) buffer;
sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
#ifndef IOS
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png");
#endif
free(buffer);
struct ReadPixelUserData {
std::function<void(void*)> onRead;
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
dapi.readPixels(rt, 0, 0, kTexWidth, kTexHeight, std::move(pb));
ReadPixelUserData* userdata = new ReadPixelUserData { output };
auto cb = [](void* buffer, size_t size, void* user) {
ReadPixelUserData* data = (ReadPixelUserData*) user;
data->onRead(buffer);
free(buffer);
delete data;
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
userdata);
dapi.readPixels(rt, 0, 0, readWidth, readHeight, std::move(pb));
}
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> renderTargets[kNumLevels],
uint32_t target, uint32_t numFrame, bool downscale, test::Backend backend,
std::string fname="") {
Handle<HwRenderTarget> rt = renderTargets[target];
size_t const readWidth = kTexWidth >> target;
size_t const readHeight = kTexHeight >> target;
size_t const size = readWidth * readHeight * 4;
if (fname.empty()) {
std::string backendStr = "gl";
if (backend == test::Backend::METAL) {
backendStr = "mtl";
} else if (backend == test::Backend::VULKAN) {
backendStr = "vk";
}
std::string const down = downscale ? "down" : "up";
fname = "feedback_" + backendStr + "_" + std::to_string(numFrame) + "_" + down + "_" +
std::to_string(target) + ".png";
}
utils::slog.e <<"writing: " << fname << utils::io::endl;
readScreenshot(dapi, renderTargets, target,
[w = readWidth, h = readHeight, size, fname](void* buffer) {
uint32_t const* texels = (uint32_t*) buffer;
sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
#ifndef IOS
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm(fname.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", fname.c_str());
#endif
});
}
// TODO: This test needs work to get Metal and OpenGL to agree on results.
@@ -131,6 +180,8 @@ TEST_F(BackendTest, FeedbackLoops) {
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
bool const backendIsVulkan = sBackend == test::Backend::VULKAN;
// Create a program.
ProgramHandle program;
{
@@ -139,10 +190,12 @@ TEST_F(BackendTest, FeedbackLoops) {
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(transformShaderText(fullscreenVs),
transformShaderText(fullscreenFs), sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
Program::Sampler psamplers[] = {utils::CString("test_tex"), 0};
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers,
sizeof(psamplers) / sizeof(psamplers[0]));
prog.uniformBlockBindings({{"params", 1}});
program = api.createProgram(std::move(prog));
}
@@ -167,10 +220,25 @@ TEST_F(BackendTest, FeedbackLoops) {
const size_t size = kTexHeight * kTexWidth * 4;
uint8_t* buffer = (uint8_t*) malloc(size);
for (int r = 0, i = 0; r < kTexHeight; r++) {
int const adjustedR = backendIsVulkan ? r : kTexHeight - r - 1;
for (int c = 0; c < kTexWidth; c++, i += 4) {
auto hval = 0x1f * c / (kTexWidth - 1);
if (hval > 0x05) {
hval = 0x1f;
} else {
hval = 0x00;
}
auto vval = 0x1f * adjustedR / (kTexHeight - 1);
if (vval > 0x05) {
vval = 0x1f;
} else {
vval = 0x00;
}
buffer[i + 0] = 0x10;
buffer[i + 1] = 0x1f * r / (kTexHeight - 1);
buffer[i + 1] = 0x1f * adjustedR / (kTexHeight - 1);
// buffer[i + 1] = vval;
buffer[i + 2] = 0x1f * c / (kTexWidth - 1);
// buffer[i + 2] = hval;
buffer[i + 3] = 0xf0;
}
}
@@ -179,7 +247,6 @@ TEST_F(BackendTest, FeedbackLoops) {
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
for (int frame = 0; frame < kNumFrames; frame++) {
// Prep for rendering.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::NONE;
@@ -191,8 +258,9 @@ TEST_F(BackendTest, FeedbackLoops) {
state.program = program;
SamplerGroup samplers(1);
SamplerParams sparams = {};
sparams.filterMag = SamplerMagFilter::LINEAR;
sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
sparams.filterMag = SamplerMagFilter::NEAREST;
// sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
sparams.filterMin = SamplerMinFilter::NEAREST;
samplers.setSampler(0, { texture, sparams });
auto sgroup =
api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test"));
@@ -207,19 +275,24 @@ TEST_F(BackendTest, FeedbackLoops) {
// Downsample passes
params.flags.discardStart = TargetBufferFlags::ALL;
state.rasterState.disableBlending();
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
const uint32_t sourceLevel = targetLevel - 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
uploadUniforms(getDriverApi(), ubuffer, {
.fbWidth = float(params.viewport.width),
.fbHeight = float(params.viewport.height),
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
for (int targetLevel = 0; targetLevel < kNumLevels; targetLevel++) {
if (targetLevel > 0) {
const uint32_t sourceLevel = targetLevel - 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel);
uploadUniforms(getDriverApi(), ubuffer, {
.fbWidth = float(params.viewport.width),
.fbHeight = float(params.viewport.height),
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
#if DUMP_INTERMEDIATE_SCREENSHOTS
dumpScreenshot(api, renderTargets, targetLevel, frame, true, sBackend);
#endif
}
// Upsample passes
@@ -239,16 +312,22 @@ TEST_F(BackendTest, FeedbackLoops) {
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
#if DUMP_INTERMEDIATE_SCREENSHOTS
dumpScreenshot(api, renderTargets, targetLevel, frame, false, sBackend);
#endif
}
getDriverApi().setMinMaxLevels(texture, 0, 0x7f);
getDriverApi().destroySamplerGroup(sgroup);
getDriverApi().destroyBufferObject(ubuffer);
// Read back the render target corresponding to the base level.
//
// NOTE: Calling glReadPixels on any miplevel other than the base level
// seems to be un-reliable on some GPU's.
if (frame == kNumFrames - 1) {
dumpScreenshot(api, renderTargets[0]);
dumpScreenshot(api, renderTargets, 0, frame, false, sBackend, "Feedback.png");
}
api.flush();

View File

@@ -50,11 +50,11 @@ class UTILS_PUBLIC Material : public FilamentAPI {
struct BuilderDetails;
public:
using BlendingMode = filament::BlendingMode;
using Shading = filament::Shading;
using Interpolation = filament::Interpolation;
using VertexDomain = filament::VertexDomain;
using TransparencyMode = filament::TransparencyMode;
using BlendingMode = BlendingMode;
using Shading = Shading;
using Interpolation = Interpolation;
using VertexDomain = VertexDomain;
using TransparencyMode = TransparencyMode;
using ParameterType = backend::UniformType;
using Precision = backend::Precision;

View File

@@ -66,26 +66,26 @@ class Viewport;
*/
class UTILS_PUBLIC View : public FilamentAPI {
public:
using QualityLevel = filament::QualityLevel;
using BlendMode = filament::BlendMode;
using AntiAliasing = filament::AntiAliasing;
using Dithering = filament::Dithering;
using ShadowType = filament::ShadowType;
using QualityLevel = QualityLevel;
using BlendMode = BlendMode;
using AntiAliasing = AntiAliasing;
using Dithering = Dithering;
using ShadowType = ShadowType;
using DynamicResolutionOptions = filament::DynamicResolutionOptions;
using BloomOptions = filament::BloomOptions;
using FogOptions = filament::FogOptions;
using DepthOfFieldOptions = filament::DepthOfFieldOptions;
using VignetteOptions = filament::VignetteOptions;
using RenderQuality = filament::RenderQuality;
using AmbientOcclusionOptions = filament::AmbientOcclusionOptions;
using TemporalAntiAliasingOptions = filament::TemporalAntiAliasingOptions;
using MultiSampleAntiAliasingOptions = filament::MultiSampleAntiAliasingOptions;
using VsmShadowOptions = filament::VsmShadowOptions;
using SoftShadowOptions = filament::SoftShadowOptions;
using ScreenSpaceReflectionsOptions = filament::ScreenSpaceReflectionsOptions;
using GuardBandOptions = filament::GuardBandOptions;
using StereoscopicOptions = filament::StereoscopicOptions;
using DynamicResolutionOptions = DynamicResolutionOptions;
using BloomOptions = BloomOptions;
using FogOptions = FogOptions;
using DepthOfFieldOptions = DepthOfFieldOptions;
using VignetteOptions = VignetteOptions;
using RenderQuality = RenderQuality;
using AmbientOcclusionOptions = AmbientOcclusionOptions;
using TemporalAntiAliasingOptions = TemporalAntiAliasingOptions;
using MultiSampleAntiAliasingOptions = MultiSampleAntiAliasingOptions;
using VsmShadowOptions = VsmShadowOptions;
using SoftShadowOptions = SoftShadowOptions;
using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions;
using GuardBandOptions = GuardBandOptions;
using StereoscopicOptions = StereoscopicOptions;
/**
* Sets the View's name. Only useful for debugging.

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.49.0"
spec.version = "1.47.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.49.0/filament-v1.49.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.47.0/filament-v1.47.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -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 = 49;
static constexpr size_t MATERIAL_VERSION = 47;
/**
* Supported shading models

View File

@@ -912,8 +912,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
if (targetApi == TargetApi::OPENGL) {
if (targetLanguage == TargetLanguage::SPIRV) {
ShaderGenerator::fixupExternalSamplers(shaderModel, shader, featureLevel,
info);
ShaderGenerator::fixupExternalSamplers(shaderModel, shader, info);
}
}
@@ -1163,6 +1162,7 @@ error:
mVariantFilter |= uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING);
mVariantFilter |= uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING);
mVariantFilter |= uint32_t(UserVariantFilterBit::SHADOW_RECEIVER);
mVariantFilter |= uint32_t(UserVariantFilterBit::SKINNING);
mVariantFilter |= uint32_t(UserVariantFilterBit::VSM);
mVariantFilter |= uint32_t(UserVariantFilterBit::SSR);
mVariantFilter |= uint32_t(UserVariantFilterBit::STE);

View File

@@ -14,8 +14,6 @@
* limitations under the License.
*/
#include "glslang/Include/intermediate.h"
const TBuiltInResource DefaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,

View File

@@ -778,7 +778,7 @@ io::sstream& CodeGenerator::generateSubpass(io::sstream& out, SubpassInfo subpas
}
void CodeGenerator::fixupExternalSamplers(
std::string& shader, SamplerInterfaceBlock const& sib, FeatureLevel featureLevel) noexcept {
std::string& shader, SamplerInterfaceBlock const& sib) noexcept {
auto const& infos = sib.getSamplerInfoList();
if (infos.empty()) {
return;
@@ -813,10 +813,7 @@ void CodeGenerator::fixupExternalSamplers(
while (shader[index] != '\n') index++;
index++;
const char *extensionLine = (featureLevel >= FeatureLevel::FEATURE_LEVEL_1)
? "#extension GL_OES_EGL_image_external_essl3 : require\n\n"
: "#extension GL_OES_EGL_image_external : require\n\n";
shader.insert(index, extensionLine);
shader.insert(index, "#extension GL_OES_EGL_image_external_essl3 : require\n");
}
}

View File

@@ -161,8 +161,7 @@ public:
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderStage type);
static void fixupExternalSamplers(
std::string& shader, filament::SamplerInterfaceBlock const& sib,
FeatureLevel featureLevel) noexcept;
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
// These constants must match the equivalent in MetalState.h.
// These values represent the starting index for uniform, ssbo, and sampler group [[buffer(n)]]

View File

@@ -23,7 +23,6 @@
#include <utils/CString.h>
#include "backend/DriverEnums.h"
#include "filamat/MaterialBuilder.h"
#include "CodeGenerator.h"
#include "SibGenerator.h"
@@ -57,7 +56,7 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream&
switch (stage) {
case ShaderStage::VERTEX:
CodeGenerator::generateDefine(out, "VARIANT_HAS_SKINNING_OR_MORPHING",
hasSkinningOrMorphing(variant, featureLevel));
variant.hasSkinningOrMorphing());
break;
case ShaderStage::FRAGMENT:
CodeGenerator::generateDefine(out, "VARIANT_HAS_FOG",
@@ -343,13 +342,12 @@ ShaderGenerator::ShaderGenerator(
}
}
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm, std::string& shader,
MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material) noexcept {
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm,
std::string& shader, MaterialInfo const& material) noexcept {
// External samplers are only supported on GL ES at the moment, we must
// skip the fixup on desktop targets
if (material.hasExternalSamplers && sm == ShaderModel::MOBILE) {
CodeGenerator::fixupExternalSamplers(shader, material.sib, featureLevel);
CodeGenerator::fixupExternalSamplers(shader, material.sib);
}
}
@@ -398,7 +396,7 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
generateSurfaceMaterialVariantProperties(vs, mProperties, mDefines);
AttributeBitset attributes = material.requiredAttributes;
if (hasSkinningOrMorphing(variant, featureLevel)) {
if (variant.hasSkinningOrMorphing()) {
attributes.set(VertexAttribute::BONE_INDICES);
attributes.set(VertexAttribute::BONE_WEIGHTS);
if (material.useLegacyMorphing) {
@@ -438,7 +436,7 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
UniformBindingPoints::SHADOW, UibGenerator::getShadowUib());
}
if (hasSkinningOrMorphing(variant, featureLevel)) {
if (variant.hasSkinningOrMorphing()) {
cg.generateUniforms(vs, ShaderStage::VERTEX,
UniformBindingPoints::PER_RENDERABLE_BONES,
UibGenerator::getPerRenderableBonesUib());
@@ -749,13 +747,4 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
return fs.c_str();
}
bool ShaderGenerator::hasSkinningOrMorphing(
filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept {
return variant.hasSkinningOrMorphing()
// HACK(exv): Ignore skinning/morphing variant when targeting ESSL 1.0. We should
// either properly support skinning on FL0 or build a system in matc which allows
// the set of included variants to differ per-feature level.
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
}
} // namespace filament

View File

@@ -75,7 +75,6 @@ public:
* the optimizations have been applied.
*/
static void fixupExternalSamplers(filament::backend::ShaderModel sm, std::string& shader,
MaterialBuilder::FeatureLevel featureLevel,
MaterialInfo const& material) noexcept;
private:
@@ -111,10 +110,6 @@ private:
static void appendShader(utils::io::sstream& ss,
const utils::CString& shader, size_t lineOffset) noexcept;
static bool hasSkinningOrMorphing(
filament::Variant variant,
MaterialBuilder::FeatureLevel featureLevel) noexcept;
MaterialBuilder::PropertyList mProperties;
MaterialBuilder::VariableList mVariables;
MaterialBuilder::OutputList mOutputs;

View File

@@ -1,5 +0,0 @@
This project is pulled in as a dependency for spirv-tools. The Vulkan
backend also has a dependency on this project. This project should not
be updated directly, instead, please see
/third_party/spirv-tools/FILAMENT_README.md for instructions for
update.

View File

@@ -1,27 +0,0 @@
diff --git a/third_party/spirv-headers/CMakeLists.txt b/third_party/spirv-headers/CMakeLists.txt
index 9cfba73af..2b8bb5e59 100644
--- a/third_party/spirv-headers/CMakeLists.txt
+++ b/third_party/spirv-headers/CMakeLists.txt
@@ -28,7 +28,7 @@
# The SPIR-V headers from the SPIR-V Registry
# https://www.khronos.org/registry/spir-v/
#
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.19)
project(SPIRV-Headers VERSION 1.5.5)
# There are two ways to use this project.
@@ -44,11 +44,9 @@ project(SPIRV-Headers VERSION 1.5.5)
# 2. cmake ..
# 3. cmake --build . --target install
-option(SPIRV_HEADERS_SKIP_EXAMPLES "Skip building examples"
- ${SPIRV_HEADERS_SKIP_EXAMPLES})
+option(SPIRV_HEADERS_SKIP_EXAMPLES "Skip building examples" ON)
-option(SPIRV_HEADERS_SKIP_INSTALL "Skip install"
- ${SPIRV_HEADERS_SKIP_INSTALL})
+option(SPIRV_HEADERS_SKIP_INSTALL "Skip install" ON)
if(NOT ${SPIRV_HEADERS_SKIP_EXAMPLES})
set(SPIRV_HEADERS_ENABLE_EXAMPLES ON)

View File

@@ -0,0 +1,14 @@
name: Wasm Build
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build web
run: docker-compose up
- name: Run tests
run: node test/wasm/test.js

View File

@@ -5,8 +5,6 @@ compile_commands.json
/build*/
/buildtools/
/external/googletest
/external/SPIRV-Headers
/external/spirv-headers
/external/effcee
/external/re2
/external/protobuf

View File

@@ -24,8 +24,6 @@ if (POLICY CMP0054)
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(SPIRV-Headers_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../spirv-headers)
if (APPLE)
set(CMAKE_MACOSX_RPATH ON)
endif (APPLE)
@@ -108,7 +106,6 @@ set(SPIRV_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to c
option(SPIRV_BUILD_LIBFUZZER_TARGETS "Build libFuzzer targets" OFF)
set(SPIRV_WERROR OFF)
if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))
set(COMPILER_IS_LIKE_GNU TRUE)
endif()
@@ -316,6 +313,8 @@ option(SPIRV_SKIP_EXECUTABLES
${SPIRV_SKIP_EXECUTABLES_OPTION})
option(SPIRV_SKIP_TESTS
"Skip building tests along with the library" ${SPIRV_SKIP_TESTS_OPTION})
if ("${SPIRV_SKIP_EXECUTABLES}")
set(SPIRV_SKIP_TESTS ON)
endif()
@@ -379,3 +378,4 @@ endif()
set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link")
set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared")

View File

@@ -1,20 +1,34 @@
When updating SPIRV-Tools to a new version, run the following from *this* directory
When updating spirv-tools to a new version there are several `CMakeLists.txt` files that need
to patched with Filament specific changes. This can be done by running the following command at
Filament's root:
```
bash filament-update.sh [git commit hash]
git apply third_party/spirv-tools/filament-specific-changes.patch
```
This will pull in the updated source for SPRIV-Tools and pull in the right version of SPIRV-Headers.
The script will also try to apply Filament specific changes to the `CMakeLists.txt`. It could be
that the diff application will fail, in which case, the updater will need to resolve the difference
manually and update `filament-specific-changes.patch`.
The above script will bring in the changes, but you would still need to add it to a git commit
(i.e. pull request) by doing
The following procedure can be used to update spirv-tools. Note that there is a secondary repository
that needs to be downloaded (spirv-headers).
```
git add -u third_party/spriv-tools third_party/spirv-headers
curl -L https://github.com/KhronosGroup/spirv-tools/archive/master.zip > master.zip
unzip master.zip
rsync -r SPIRV-Tools-master/ spirv-tools/ --delete
rm -rf SPIRV-Tools-master master.zip
curl -L https://github.com/KhronosGroup/spirv-headers/archive/master.zip > master.zip
unzip master.zip
mv SPIRV-Headers-master/ spirv-tools/external/spirv-headers
rm master.zip
git add spirv-tools
```
from the Filament source root.
The patch file also edits the `.gitignore` to allow `spirv-headers` to be committed.
Finally, remember to bring back the Filament-specific changes in CMakeLists.
To restore this file and the patch file do:
```
git checkout main spirv-tools/FILAMENT_README.md
git checkout main spirv-tools/filament-specific-changes.patch
```

View File

@@ -45,8 +45,6 @@ if (IS_DIRECTORY ${SPIRV_HEADER_DIR})
# Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find
# headers to include.
if (NOT DEFINED SPIRV-Headers_SOURCE_DIR)
set(SPIRV_HEADERS_SKIP_INSTALL ON)
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
add_subdirectory(${SPIRV_HEADER_DIR})
endif()
else()

View File

@@ -0,0 +1,37 @@
name: Presubmit
on: [push, pull_request]
jobs:
build:
name: Build ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Install Ubuntu packages
if: matrix.os == 'ubuntu-latest'
run: sudo apt install -y dos2unix
- name: Install macOS packages
if: matrix.os == 'macos-latest'
run: brew install dos2unix
- name: Build
run: |
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=install ..
cmake --build . --target install
- name: Build spec tools
run: |
cd tools/buildHeaders
mkdir build
cd build
cmake ..
cmake --build . --target install
- name: Build headers
run: |
cd tools/buildHeaders
./bin/makeHeaders
- name: Check generated headers
run: git diff --exit-code

Some files were not shown because too many files have changed in this diff Show More