Merge branch 'rc/1.9.7' into release
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ civetweb.txt
|
||||
settings.json
|
||||
test*.png
|
||||
test*.json
|
||||
results
|
||||
|
||||
16
BUILDING.md
16
BUILDING.md
@@ -468,3 +468,19 @@ export SWIFTSHADER_LD_LIBRARY_PATH=`pwd`
|
||||
```
|
||||
|
||||
Next, go to your Filament repo and use the [easy build](#easy-build) script with `-t`.
|
||||
|
||||
## SwiftShader for CI
|
||||
|
||||
Continuous testing turnaround can be quite slow if you need to build SwiftShader from scratch, so we
|
||||
provide an Ubuntu-based Docker image that has it already built. The Docker image also includes
|
||||
everything necessary for building Filament. You can fetch and run the image as follows:
|
||||
|
||||
```
|
||||
docker pull ghcr.io/filament-assets/swiftshader
|
||||
docker run -it ghcr.io/filament-assets/swiftshader
|
||||
```
|
||||
|
||||
To do more with the container, see the helper script at `build/swiftshader/test.sh`.
|
||||
|
||||
If you are a team member, you can update the public image to the latest SwiftShader by
|
||||
following the instructions at the top of `build/swiftshader/Dockerfile`.
|
||||
|
||||
@@ -21,6 +21,10 @@ option(FILAMENT_ENABLE_LTO "Enable link-time optimizations if supported by the c
|
||||
|
||||
option(FILAMENT_SKIP_SAMPLES "Don't build samples" OFF)
|
||||
|
||||
option(FILAMENT_SUPPORTS_XCB "Include XCB support in Linux builds" ON)
|
||||
|
||||
option(FILAMENT_SUPPORTS_XLIB "Include XLIB support in Linux builds" ON)
|
||||
|
||||
set(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB "2" CACHE STRING
|
||||
"Per render pass arena size. Must be roughly 1 MB larger than FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB, default 2."
|
||||
)
|
||||
@@ -84,6 +88,15 @@ if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT WEBGL)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
|
||||
if (FILAMENT_SUPPORTS_XCB)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_XCB)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_XLIB)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_XLIB)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND uname -p
|
||||
OUTPUT_VARIABLE PROCESSOR_ARCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.9.6'
|
||||
implementation 'com.google.android.filament:filament-android:1.9.7'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ A much smaller alternative to `filamat-android` that can only generate OpenGL sh
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.9.6'
|
||||
pod 'Filament', '~> 1.9.7'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -5,6 +5,14 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## Next release (main branch)
|
||||
|
||||
## v1.9.7
|
||||
|
||||
- Vulkan: improvements to the ReadPixels implementation.
|
||||
- Vulkan: warn instead of panic for sampler overflow.
|
||||
- Vulkan: fix leak with headless swap chain.
|
||||
- PlatformVkLinux now supports all combos of XLIB and XCB.
|
||||
- Fix TypeScript binding for TextureUsage.
|
||||
|
||||
## v1.9.6
|
||||
|
||||
- Added View::setVsmShadowOptions (experimental)
|
||||
|
||||
@@ -85,6 +85,12 @@ public class SwapChain {
|
||||
*/
|
||||
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;
|
||||
|
||||
SwapChain(long nativeSwapChain, Object surface) {
|
||||
mNativeObject = nativeSwapChain;
|
||||
mSurface = surface;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.9.6
|
||||
VERSION_NAME=1.9.7
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
53
build/swiftshader/Dockerfile
Normal file
53
build/swiftshader/Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
||||
# Build the image:
|
||||
# docker build --no-cache --tag ssfilament -f build/swiftshader/Dockerfile .
|
||||
# docker tag ssfilament ghcr.io/filament-assets/swiftshader
|
||||
#
|
||||
# Publish the image:
|
||||
# docker login ghcr.io --username <user> --password <token>
|
||||
# docker push ghcr.io/filament-assets/swiftshader
|
||||
#
|
||||
# Run the image and mount the current directory:
|
||||
# docker run -it -v `pwd`:/trees/filament -t ssfilament
|
||||
|
||||
FROM ubuntu:focal
|
||||
WORKDIR /trees
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV SWIFTSHADER_LD_LIBRARY_PATH=/trees/swiftshader/build
|
||||
ENV CXXFLAGS='-fno-builtin -Wno-pass-failed'
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get --no-install-recommends install -y \
|
||||
apt-transport-https \
|
||||
apt-utils \
|
||||
build-essential \
|
||||
cmake \
|
||||
ca-certificates \
|
||||
git \
|
||||
ninja-build \
|
||||
python \
|
||||
python3 \
|
||||
xorg-dev \
|
||||
clang-7 \
|
||||
libc++-7-dev \
|
||||
libc++abi-7-dev \
|
||||
lldb
|
||||
|
||||
# Ensure that clang is used instead of gcc.
|
||||
RUN set -eux ;\
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-7 100 ;\
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-7 100 ;\
|
||||
update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 ;\
|
||||
update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
|
||||
|
||||
# Get patch files from the local Filament tree.
|
||||
COPY build/swiftshader/*.diff .
|
||||
|
||||
# Clone SwiftShader, apply patches, and build it.
|
||||
RUN set -eux ;\
|
||||
git clone https://swiftshader.googlesource.com/SwiftShader swiftshader ;\
|
||||
cd swiftshader ;\
|
||||
git checkout 139f5c3 ;\
|
||||
git apply /trees/*.diff ;\
|
||||
cd build ;\
|
||||
cmake .. -GNinja -DCMAKE_BUILD_TYPE=Release ;\
|
||||
ninja
|
||||
56
build/swiftshader/gallery.py
Executable file
56
build/swiftshader/gallery.py
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
spath = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
path = Path(spath)
|
||||
|
||||
folder = "../../results/"
|
||||
|
||||
images = list(path.glob(folder + '*.png'))
|
||||
|
||||
images.sort()
|
||||
|
||||
gallery = open(path.absolute().joinpath(folder + 'index.html'), 'w')
|
||||
|
||||
gallery.write("""<html>
|
||||
<head>
|
||||
<script type="module" src="https://unpkg.com/img-comparison-slider@latest/dist/component/component.esm.js"></script>
|
||||
<script nomodule="" src="https://unpkg.com/img-comparison-slider@latest/dist/component/component.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/img-comparison-slider@latest/dist/collection/styles/initial.css"/>
|
||||
<style>
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
margin-top: 150px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: blue;
|
||||
}
|
||||
a:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
""")
|
||||
|
||||
tag = ''
|
||||
|
||||
for image in images:
|
||||
group = image.stem.rstrip('0123456789')
|
||||
before = f'https://filament-assets.github.io/golden/{group}/{image.name}'
|
||||
after = image.name
|
||||
gallery.write('\n')
|
||||
gallery.write(f'<h2><a href="{image.stem}.json">{image.stem}.json</a></h2>\n')
|
||||
gallery.write('<img-comparison-slider>\n')
|
||||
gallery.write(f'<img slot="before" src="{before}" /> <img slot="after" src="{after}" />\n')
|
||||
gallery.write('</img-comparison-slider>\n')
|
||||
|
||||
gallery.write("""</body>
|
||||
</html>
|
||||
""")
|
||||
62
build/swiftshader/patch_00.diff
Normal file
62
build/swiftshader/patch_00.diff
Normal file
@@ -0,0 +1,62 @@
|
||||
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
|
||||
index 86913ec72..3b35345af 100644
|
||||
--- a/src/Vulkan/VkPipeline.cpp
|
||||
+++ b/src/Vulkan/VkPipeline.cpp
|
||||
@@ -71,7 +71,56 @@ std::vector<uint32_t> preprocessSpirv(
|
||||
if(optimize)
|
||||
{
|
||||
// Full optimization list taken from spirv-opt.
|
||||
- opt.RegisterPerformancePasses();
|
||||
+
|
||||
+ // We have removed CreateRedundancyEliminationPass because it segfaults when encountering:
|
||||
+ // %389 = OpCompositeConstruct %7 %386 %387 %388 %86
|
||||
+ // When inserting an entry into instruction_to_value_ (which is an unordered_map)
|
||||
+ // This could perhaps be investigated further with help from asan.
|
||||
+
|
||||
+ using namespace spvtools;
|
||||
+ opt.RegisterPass(CreateWrapOpKillPass())
|
||||
+ .RegisterPass(CreateDeadBranchElimPass())
|
||||
+ .RegisterPass(CreateMergeReturnPass())
|
||||
+ .RegisterPass(CreateInlineExhaustivePass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreatePrivateToLocalPass())
|
||||
+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
||||
+ .RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateScalarReplacementPass())
|
||||
+ .RegisterPass(CreateLocalAccessChainConvertPass())
|
||||
+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
||||
+ .RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateLocalMultiStoreElimPass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateCCPPass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateLoopUnrollPass(true))
|
||||
+ .RegisterPass(CreateDeadBranchElimPass())
|
||||
+ .RegisterPass(CreateRedundancyEliminationPass()) // workaround for SEGFAULT
|
||||
+ .RegisterPass(CreateCombineAccessChainsPass())
|
||||
+ .RegisterPass(CreateSimplificationPass())
|
||||
+ .RegisterPass(CreateScalarReplacementPass())
|
||||
+ .RegisterPass(CreateLocalAccessChainConvertPass())
|
||||
+ .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
||||
+ .RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateSSARewritePass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateVectorDCEPass())
|
||||
+ .RegisterPass(CreateDeadInsertElimPass())
|
||||
+ .RegisterPass(CreateDeadBranchElimPass())
|
||||
+ .RegisterPass(CreateSimplificationPass())
|
||||
+ .RegisterPass(CreateIfConversionPass())
|
||||
+ .RegisterPass(CreateCopyPropagateArraysPass())
|
||||
+ .RegisterPass(CreateReduceLoadSizePass())
|
||||
+ .RegisterPass(CreateAggressiveDCEPass())
|
||||
+ .RegisterPass(CreateBlockMergePass())
|
||||
+ .RegisterPass(CreateRedundancyEliminationPass()) // workaround for SEGFAULT
|
||||
+ .RegisterPass(CreateDeadBranchElimPass())
|
||||
+ .RegisterPass(CreateBlockMergePass())
|
||||
+ .RegisterPass(CreateSimplificationPass());
|
||||
}
|
||||
|
||||
std::vector<uint32_t> optimized;
|
||||
127
build/swiftshader/test.sh
Executable file
127
build/swiftshader/test.sh
Executable file
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
function print_help {
|
||||
local self_name=$(basename "$0")
|
||||
echo "This script issues docker commands for testing Filament with SwiftShader."
|
||||
echo "The usual sequence of commands is: fetch, start, build filament release, and run."
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $self_name [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " build filament [debug | release]"
|
||||
echo " Use the container to build Filament."
|
||||
echo " build swiftshader [debug | release]"
|
||||
echo " Use the container to do a clean rebuild of SwiftShader."
|
||||
echo " (Note that the container already has SwiftShader built.)"
|
||||
echo " fetch"
|
||||
echo " Download the docker image from the central repository."
|
||||
echo " help"
|
||||
echo " Print this help message."
|
||||
echo " logs"
|
||||
echo " Print messages from the container's kernel ring buffer."
|
||||
echo " This is useful for diagnosing OOM issues."
|
||||
echo " run [lldb]"
|
||||
echo " Launch a test inside the container, optionally via lldb."
|
||||
echo " shell"
|
||||
echo " Interact with a bash prompt in the container."
|
||||
echo " start"
|
||||
echo " Start a container from the image."
|
||||
echo " stop"
|
||||
echo " Stop the container."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Change the current working directory to the Filament root.
|
||||
pushd "$(dirname "$0")/../.." > /dev/null
|
||||
|
||||
if [[ "$1" == "build" ]] && [[ "$2" == "filament" ]]; then
|
||||
docker exec runner filament/build.sh -t $3 gltf_viewer
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "build" ]] && [[ "$2" == "swiftshader" ]]; then
|
||||
BUILD_TYPE="$3"
|
||||
BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}"
|
||||
docker exec --workdir /trees/swiftshader runner rm -rf build
|
||||
docker exec --workdir /trees/swiftshader runner mkdir build
|
||||
docker exec --workdir /trees/swiftshader/build runner cmake -GNinja -DCMAKE_BUILD_TYPE="$BUILD_TYPE" ..
|
||||
docker exec --workdir /trees/swiftshader/build runner ninja
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "fetch" ]]; then
|
||||
docker pull ghcr.io/filament-assets/swiftshader:latest
|
||||
docker tag ghcr.io/filament-assets/swiftshader:latest ssfilament
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "help" ]]; then
|
||||
print_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" == "logs" ]]; then
|
||||
docker exec runner dmesg --human --read-clear
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "run" ]] && [[ "$2" == "lldb" ]]; then
|
||||
docker exec -i --workdir /trees/filament/results runner \
|
||||
lldb --batch -o run -o bt -- \
|
||||
../out/cmake-release/samples/gltf_viewer \
|
||||
--headless \
|
||||
--batch ../libs/viewer/tests/basic.json \
|
||||
--api vulkan
|
||||
docker exec runner /trees/filament/build/swiftshader/gallery.py
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "run" ]]; then
|
||||
docker exec --tty --workdir /trees/filament/results runner \
|
||||
/usr/bin/catchsegv \
|
||||
../out/cmake-release/samples/gltf_viewer \
|
||||
--headless \
|
||||
--batch ../libs/viewer/tests/basic.json \
|
||||
--api vulkan
|
||||
docker exec runner /trees/filament/build/swiftshader/gallery.py
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "shell" ]]; then
|
||||
docker exec --interactive --tty runner /bin/bash
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Notes on options being passed to docker's run command:
|
||||
#
|
||||
# - The memory constraint seems to prevent an OOM signal in GitHub Actions.
|
||||
# - The cap / security args allow use of lldb and creation of core dumps.
|
||||
# - The privileged arg allows use of dmesg for examining OOM logs.
|
||||
#
|
||||
# Currently, a GitHub Actions VM has 2 CPUs, 7 GB RAM, and 14 GB of SSD disk space.
|
||||
#
|
||||
# Please be aware that Docker Desktop might impose additional resource constraints, and that those
|
||||
# settings can only be controlled with its GUI. We recommend at least 7 GB of memory and 2 GB swap.
|
||||
if [[ "$1" == "start" ]]; then
|
||||
mkdir -p results
|
||||
docker run --tty --rm --detach --privileged \
|
||||
--memory 6.5g \
|
||||
--name runner \
|
||||
--cap-add=SYS_PTRACE \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
--volume `pwd`:/trees/filament \
|
||||
--workdir /trees \
|
||||
ssfilament
|
||||
exit $?
|
||||
fi
|
||||
|
||||
if [[ "$1" == "stop" ]]; then
|
||||
docker container rm runner --force
|
||||
exit $?
|
||||
fi
|
||||
|
||||
print_help
|
||||
exit 1
|
||||
@@ -326,6 +326,11 @@ if (APPLE)
|
||||
spirv-cross-msl
|
||||
)
|
||||
|
||||
if (NOT IOS)
|
||||
target_link_libraries(backend_test PRIVATE image imageio)
|
||||
list(APPEND BACKEND_TEST_DEPS image imageio)
|
||||
endif()
|
||||
|
||||
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
|
||||
combine_static_libs(backend_test "${BACKEND_TEST_COMBINED_OUTPUT}" "${BACKEND_TEST_DEPS}")
|
||||
|
||||
@@ -337,7 +342,7 @@ if (APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS AND NOT FILAMENT_USE_SWIFTSHADER)
|
||||
if (APPLE AND NOT IOS)
|
||||
add_executable(backend_test_mac test/mac_runner.mm)
|
||||
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
|
||||
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace backend {
|
||||
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
|
||||
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
|
||||
|
||||
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t MAX_SAMPLER_COUNT = 16; // Matches the Adreno Vulkan driver.
|
||||
@@ -215,6 +216,11 @@ enum class SamplerType : uint8_t {
|
||||
SAMPLER_3D, //!< 3D texture
|
||||
};
|
||||
|
||||
//! Subpass type
|
||||
enum class SubpassType : uint8_t {
|
||||
SUBPASS_INPUT
|
||||
};
|
||||
|
||||
//! Texture sampler format
|
||||
enum class SamplerFormat : uint8_t {
|
||||
INT = 0, //!< signed integer sampler
|
||||
|
||||
@@ -24,19 +24,24 @@
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
// This little utility adds padding to multi-channel interleaved data by inserting dummy values, or
|
||||
// discards trailing channels. This is useful for platforms that only accept 4-component data, since
|
||||
// users often wish to submit (or receive) 3-component data.
|
||||
// Provides an alpha value when expanding 3-channel images to 4-channel.
|
||||
// Also used as a normalization scale when converting between numeric types.
|
||||
template<typename componentType> inline componentType getMaxValue();
|
||||
|
||||
class DataReshaper {
|
||||
public:
|
||||
template<typename componentType, size_t srcChannelCount, size_t dstChannelCount,
|
||||
componentType maxValue = std::numeric_limits<componentType>::max()>
|
||||
|
||||
// Adds padding to multi-channel interleaved data by inserting dummy values, or discards
|
||||
// trailing channels. This is useful for platforms that only accept 4-component data, since
|
||||
// users often wish to submit (or receive) 3-component data.
|
||||
template<typename componentType, size_t srcChannelCount, size_t dstChannelCount>
|
||||
static void reshape(void* dest, const void* src, size_t numSrcBytes) {
|
||||
const componentType maxValue = getMaxValue<componentType>();
|
||||
const componentType* in = (const componentType*) src;
|
||||
componentType* out = (componentType*) dest;
|
||||
const size_t srcWordCount = (numSrcBytes / sizeof(componentType)) / srcChannelCount;
|
||||
const size_t width = (numSrcBytes / sizeof(componentType)) / srcChannelCount;
|
||||
const int minChannelCount = filament::math::min(srcChannelCount, dstChannelCount);
|
||||
for (size_t word = 0; word < srcWordCount; ++word) {
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (size_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
out[channel] = in[channel];
|
||||
}
|
||||
@@ -48,37 +53,114 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template<typename componentType, size_t srcChannelCount, size_t dstChannelCount,
|
||||
componentType maxValue = std::numeric_limits<componentType>::max()>
|
||||
static void reshapeImage(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t dstBytesPerRow, size_t height, bool swizzle03) {
|
||||
const size_t srcWordCount = (srcBytesPerRow / sizeof(componentType)) / srcChannelCount;
|
||||
const int minChannelCount = filament::math::min(srcChannelCount, dstChannelCount);
|
||||
// Converts a 4-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount, size_t height, bool swizzle, bool flip) {
|
||||
const size_t srcChannelCount = 4;
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
|
||||
const size_t width = (srcBytesPerRow / sizeof(srcComponentType)) / srcChannelCount;
|
||||
const size_t minChannelCount = filament::math::min(srcChannelCount, dstChannelCount);
|
||||
assert(minChannelCount <= 4);
|
||||
int inds[4] = {0, 1, 2, 3};
|
||||
if (swizzle03) {
|
||||
inds[0] = 2;
|
||||
inds[2] = 0;
|
||||
const int inds[4] = {swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3};
|
||||
|
||||
int srcStride;
|
||||
if (flip) {
|
||||
src += srcBytesPerRow * (height - 1);
|
||||
srcStride = -srcBytesPerRow;
|
||||
} else {
|
||||
srcStride = srcBytesPerRow;
|
||||
}
|
||||
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const componentType* in = (const componentType*) src;
|
||||
componentType* out = (componentType*) dest;
|
||||
for (size_t word = 0; word < srcWordCount; ++word) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*) dest;
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (size_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
out[channel] = in[inds[channel]];
|
||||
out[channel] = in[inds[channel]] * dstMaxValue / srcMaxValue;
|
||||
}
|
||||
for (size_t channel = srcChannelCount; channel < dstChannelCount; ++channel) {
|
||||
out[channel] = maxValue;
|
||||
out[channel] = dstMaxValue;
|
||||
}
|
||||
in += srcChannelCount;
|
||||
out += dstChannelCount;
|
||||
}
|
||||
src += srcBytesPerRow;
|
||||
src += srcStride;
|
||||
dest += dstBytesPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a 4-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
static bool reshapeImage(PixelBufferDescriptor* dst, PixelDataType srcType,
|
||||
const uint8_t* srcBytes, int srcBytesPerRow, int width, int height, bool swizzle,
|
||||
bool flip) {
|
||||
size_t dstChannelCount;
|
||||
switch (dst->format) {
|
||||
case PixelDataFormat::RGB: dstChannelCount = 3; break;
|
||||
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
|
||||
default: return false;
|
||||
}
|
||||
void (*reshaper)(uint8_t*, const uint8_t*, size_t, size_t, size_t, size_t, bool, bool)
|
||||
= nullptr;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE, FLOAT = PixelDataType::FLOAT,
|
||||
UINT = PixelDataType::UINT, INT = PixelDataType::INT;
|
||||
switch (dst->type) {
|
||||
case UBYTE:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImage<uint8_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case FLOAT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImage<float, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<float, float>; break;
|
||||
case INT: reshaper = reshapeImage<float, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<float, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case INT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImage<int32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<int32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<int32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<int32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case UINT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImage<uint32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<uint32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint8_t* dstBytes = (uint8_t*) dst->buffer;
|
||||
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
|
||||
dst->stride ? dst->stride : width, 1, dst->alignment);
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, dstBytesPerRow, dstChannelCount, height,
|
||||
swizzle, flip);
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<> inline float getMaxValue() { return 1.0f; }
|
||||
template<> inline int32_t getMaxValue() { return 0x7fffffff; }
|
||||
template<> inline uint32_t getMaxValue() { return 0xffffffff; }
|
||||
template<> inline uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
|
||||
template<> inline uint8_t getMaxValue() { return 0xff; }
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -38,8 +38,7 @@ TextureReshaper::TextureReshaper(TextureFormat requestedFormat) noexcept {
|
||||
const size_t reshapedSize = p.size / 6 * 8; // reshaping from 6 to 8 bytes per pixel
|
||||
void* reshapeBuffer = malloc(reshapedSize);
|
||||
ASSERT_POSTCONDITION(reshapeBuffer, "Could not allocate memory to reshape pixels.");
|
||||
// 0x3c00 is 1.0 in 16 bit floating point.
|
||||
DataReshaper::reshape<uint16_t, 3, 4, 0x3c00>(reshapeBuffer, p.buffer, p.size);
|
||||
DataReshaper::reshape<uint16_t, 3, 4>(reshapeBuffer, p.buffer, p.size);
|
||||
|
||||
PixelBufferDescriptor reshaped(reshapeBuffer, reshapedSize,
|
||||
PixelBufferDescriptor::PixelDataFormat::RGBA,
|
||||
|
||||
@@ -47,7 +47,7 @@ Driver* PlatformVkAndroid::createDriver(void* const sharedContext) noexcept {
|
||||
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]));
|
||||
}
|
||||
|
||||
void* PlatformVkAndroid::createVkSurfaceKHR(void* nativeWindow, void* vkinstance) noexcept {
|
||||
void* PlatformVkAndroid::createVkSurfaceKHR(void* nativeWindow, void* vkinstance, uint64_t flags) noexcept {
|
||||
const VkInstance instance = (VkInstance) vkinstance;
|
||||
ANativeWindow* aNativeWindow = (ANativeWindow*) nativeWindow;
|
||||
VkAndroidSurfaceCreateInfoKHR createInfo {
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
backend::Driver* createDriver(void* const sharedContext) noexcept override;
|
||||
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
|
||||
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace filament {
|
||||
class PlatformVkCocoa final : public backend::VulkanPlatform {
|
||||
public:
|
||||
backend::Driver* createDriver(void* sharedContext) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
};
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Driver* PlatformVkCocoa::createDriver(void* sharedContext) noexcept {
|
||||
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]));
|
||||
}
|
||||
|
||||
void* PlatformVkCocoa::createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept {
|
||||
void* PlatformVkCocoa::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
|
||||
// Obtain the CAMetalLayer-backed view.
|
||||
NSView* nsview = (__bridge NSView*) nativeWindow;
|
||||
ASSERT_POSTCONDITION(nsview, "Unable to obtain Metal-backed NSView.");
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace filament {
|
||||
class PlatformVkCocoaTouch final : public backend::VulkanPlatform {
|
||||
public:
|
||||
backend::Driver* createDriver(void* const sharedContext) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
};
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Driver* PlatformVkCocoaTouch::createDriver(void* const sharedContext) noexcept {
|
||||
sizeof(requestedExtensions) / sizeof(requestedExtensions[0]));
|
||||
}
|
||||
|
||||
void* PlatformVkCocoaTouch::createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept {
|
||||
void* PlatformVkCocoaTouch::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
|
||||
#if METAL_AVAILABLE
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
|
||||
|
||||
@@ -36,14 +36,18 @@ static constexpr const char* LIBRARY_X11 = "libX11.so.6";
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
typedef xcb_connection_t* (*XCB_CONNECT)(const char *displayname, int *screenp);
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
#endif
|
||||
|
||||
struct X11Functions {
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
XCB_CONNECT xcbConnect;
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
#endif
|
||||
void* library = nullptr;
|
||||
@@ -55,7 +59,8 @@ Driver* PlatformVkLinux::createDriver(void* const sharedContext) noexcept {
|
||||
"VK_KHR_surface",
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
"VK_KHR_xcb_surface",
|
||||
#else
|
||||
#endif
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
"VK_KHR_xlib_surface",
|
||||
#endif
|
||||
"VK_KHR_get_physical_device_properties2",
|
||||
@@ -67,42 +72,57 @@ Driver* PlatformVkLinux::createDriver(void* const sharedContext) noexcept {
|
||||
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]));
|
||||
}
|
||||
|
||||
void* PlatformVkLinux::createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept {
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
void* PlatformVkLinux::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_PRECONDITION(g_x11.library, "Unable to open X11 library.");
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen;
|
||||
mConnection = g_x11.xcbConnect(nullptr, &screen);
|
||||
}
|
||||
ASSERT_POSTCONDITION(vkCreateXcbSurfaceKHR, "Unable to load vkCreateXcbSurfaceKHR function.");
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
const uint64_t ptrval = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
VkXcbSurfaceCreateInfoKHR createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
|
||||
.connection = mConnection,
|
||||
.window = (xcb_window_t) ptrval,
|
||||
};
|
||||
VkResult result = vkCreateXcbSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
|
||||
#else
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
ASSERT_PRECONDITION(g_x11.library, "Unable to open X11 library.");
|
||||
ASSERT_POSTCONDITION(vkCreateXcbSurfaceKHR, "Unable to load vkCreateXcbSurfaceKHR function.");
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
mDisplay = g_x11.openDisplay(NULL);
|
||||
ASSERT_PRECONDITION(mDisplay, "Unable to open X11 display.");
|
||||
ASSERT_POSTCONDITION(vkCreateXlibSurfaceKHR, "Unable to load vkCreateXlibSurfaceKHR function.");
|
||||
#endif
|
||||
|
||||
}
|
||||
ASSERT_POSTCONDITION(vkCreateXlibSurfaceKHR, "Unable to load vkCreateXlibSurfaceKHR function.");
|
||||
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
const bool windowIsXCB = flags & SWAP_CHAIN_CONFIG_ENABLE_XCB;
|
||||
#else
|
||||
const bool windowIsXCB = true;
|
||||
#endif
|
||||
|
||||
if (windowIsXCB) {
|
||||
const uint64_t ptrval = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
VkXcbSurfaceCreateInfoKHR createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
|
||||
.connection = mConnection,
|
||||
.window = (xcb_window_t) ptrval,
|
||||
};
|
||||
vkCreateXcbSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
VkXlibSurfaceCreateInfoKHR createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
.dpy = mDisplay,
|
||||
.window = (Window) nativeWindow,
|
||||
};
|
||||
VkResult result = vkCreateXlibSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
|
||||
vkCreateXlibSurfaceKHR((VkInstance) instance, &createInfo, VKALLOC, &surface);
|
||||
#endif
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateXlibSurfaceKHR error.");
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
#include <xcb/xcb.h>
|
||||
#else
|
||||
#endif
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
@@ -35,14 +37,15 @@ public:
|
||||
|
||||
backend::Driver* createDriver(void* const sharedContext) noexcept override;
|
||||
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
|
||||
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
|
||||
private:
|
||||
#ifdef FILAMENT_SUPPORTS_XCB
|
||||
xcb_connection_t* mConnection;
|
||||
#else
|
||||
#endif
|
||||
#ifdef FILAMENT_SUPPORTS_XLIB
|
||||
Display* mDisplay;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ Driver* PlatformVkWindows::createDriver(void* const sharedContext) noexcept {
|
||||
sizeof(requiredInstanceExtensions) / sizeof(requiredInstanceExtensions[0]));
|
||||
}
|
||||
|
||||
void* PlatformVkWindows::createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept {
|
||||
void* PlatformVkWindows::createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept {
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
|
||||
HWND window = (HWND) nativeWindow;
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
|
||||
backend::Driver* createDriver(void* const sharedContext) noexcept override;
|
||||
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept override;
|
||||
void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept override;
|
||||
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "vulkan/VulkanBinder.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
@@ -478,9 +479,12 @@ void VulkanBinder::bindUniformBuffer(uint32_t bindingIndex, VkBuffer uniformBuff
|
||||
}
|
||||
|
||||
void VulkanBinder::bindSampler(uint32_t bindingIndex, VkDescriptorImageInfo samplerInfo) noexcept {
|
||||
ASSERT_POSTCONDITION(bindingIndex < SAMPLER_BINDING_COUNT,
|
||||
"Sampler bindings overflow: index = %d, capacity = %d.",
|
||||
bindingIndex, SAMPLER_BINDING_COUNT);
|
||||
assert(bindingIndex < SAMPLER_BINDING_COUNT);
|
||||
if (bindingIndex >= SAMPLER_BINDING_COUNT) {
|
||||
utils::slog.w << "Sampler bindings overflow: " << bindingIndex << " / "
|
||||
<< SAMPLER_BINDING_COUNT << utils::io::endl;
|
||||
return;
|
||||
}
|
||||
VkDescriptorImageInfo& imageInfo = mDescriptorKey.samplers[bindingIndex];
|
||||
if (imageInfo.sampler != samplerInfo.sampler || imageInfo.imageView != samplerInfo.imageView ||
|
||||
imageInfo.imageLayout != samplerInfo.imageLayout) {
|
||||
|
||||
@@ -22,7 +22,9 @@ namespace filament {
|
||||
namespace backend {
|
||||
|
||||
VulkanBuffer::VulkanBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VkBufferUsageFlags usage, uint32_t numBytes) : mContext(context), mStagePool(stagePool) {
|
||||
VulkanDisposer& disposer, VulkanDisposer::Key key, VkBufferUsageFlags usage,
|
||||
uint32_t numBytes) : mContext(context), mStagePool(stagePool), mDisposer(disposer),
|
||||
mDisposerKey(key) {
|
||||
// Create the VkBuffer.
|
||||
VkBufferCreateInfo bufferInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
@@ -51,6 +53,7 @@ void VulkanBuffer::loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_
|
||||
auto copyToDevice = [this, numBytes, stage] (VulkanCommandBuffer& commands) {
|
||||
VkBufferCopy region { .size = numBytes };
|
||||
vkCmdCopyBuffer(commands.cmdbuffer, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
mDisposer.acquire(mDisposerKey, commands.resources);
|
||||
|
||||
// Ensure that the copy finishes before the next draw call.
|
||||
VkBufferMemoryBarrier barrier {
|
||||
|
||||
@@ -26,14 +26,16 @@ namespace backend {
|
||||
// Encapsulates a Vulkan buffer, its attached DeviceMemory and a staging area.
|
||||
class VulkanBuffer {
|
||||
public:
|
||||
VulkanBuffer(VulkanContext& context, VulkanStagePool& stagePool, VkBufferUsageFlags usage,
|
||||
uint32_t numBytes);
|
||||
VulkanBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer,
|
||||
VulkanDisposer::Key mDisposerKey, VkBufferUsageFlags usage, uint32_t numBytes);
|
||||
~VulkanBuffer();
|
||||
void loadFromCpu(const void* cpuData, uint32_t byteOffset, uint32_t numBytes);
|
||||
VkBuffer getGpuBuffer() const { return mGpuBuffer; }
|
||||
private:
|
||||
VulkanContext& mContext;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanDisposer& mDisposer;
|
||||
VulkanDisposer::Key mDisposerKey;
|
||||
VmaAllocation mGpuMemory = VK_NULL_HANDLE;
|
||||
VkBuffer mGpuBuffer = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
@@ -244,6 +244,7 @@ void createLogicalDevice(VulkanContext& context) {
|
||||
.vkDestroyBuffer = vkDestroyBuffer,
|
||||
.vkCreateImage = vkCreateImage,
|
||||
.vkDestroyImage = vkDestroyImage,
|
||||
.vkCmdCopyBuffer = vkCmdCopyBuffer,
|
||||
.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2KHR,
|
||||
.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2KHR
|
||||
};
|
||||
@@ -366,7 +367,6 @@ void createSwapChain(VulkanContext& context, VulkanSurfaceContext& surfaceContex
|
||||
for (const VkSurfaceFormatKHR& format : surfaceContext.surfaceFormats) {
|
||||
if (format.format == VK_FORMAT_R8G8B8A8_UNORM) {
|
||||
surfaceContext.surfaceFormat = format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto compositionCaps = caps.supportedCompositeAlpha;
|
||||
@@ -474,9 +474,10 @@ void createSwapChain(VulkanContext& context, VulkanSurfaceContext& surfaceContex
|
||||
void destroySwapChain(VulkanContext& context, VulkanSurfaceContext& surfaceContext,
|
||||
VulkanDisposer& disposer) {
|
||||
waitForIdle(context);
|
||||
const VkDevice device = context.device;
|
||||
for (SwapContext& swapContext : surfaceContext.swapContexts) {
|
||||
disposer.release(swapContext.commands.resources);
|
||||
vkFreeCommandBuffers(context.device, context.commandPool, 1,
|
||||
vkFreeCommandBuffers(device, context.commandPool, 1,
|
||||
&swapContext.commands.cmdbuffer);
|
||||
|
||||
// The wrapper object for the submission fence has shared ownership semantics, so here
|
||||
@@ -487,17 +488,23 @@ void destroySwapChain(VulkanContext& context, VulkanSurfaceContext& surfaceConte
|
||||
swapContext.commands.fence.reset();
|
||||
}
|
||||
|
||||
vkDestroyImageView(context.device, swapContext.attachment.view, VKALLOC);
|
||||
// If this is headless, then we own the image and need to explicitly destroy it.
|
||||
if (!surfaceContext.swapchain) {
|
||||
vkDestroyImage(device, swapContext.attachment.image, VKALLOC);
|
||||
vkFreeMemory(device, swapContext.attachment.memory, VKALLOC);
|
||||
}
|
||||
|
||||
vkDestroyImageView(device, swapContext.attachment.view, VKALLOC);
|
||||
swapContext.commands.fence = VK_NULL_HANDLE;
|
||||
swapContext.attachment.view = VK_NULL_HANDLE;
|
||||
}
|
||||
vkDestroySwapchainKHR(context.device, surfaceContext.swapchain, VKALLOC);
|
||||
vkDestroySemaphore(context.device, surfaceContext.imageAvailable, VKALLOC);
|
||||
vkDestroySemaphore(context.device, surfaceContext.renderingFinished, VKALLOC);
|
||||
vkDestroySwapchainKHR(device, surfaceContext.swapchain, VKALLOC);
|
||||
vkDestroySemaphore(device, surfaceContext.imageAvailable, VKALLOC);
|
||||
vkDestroySemaphore(device, surfaceContext.renderingFinished, VKALLOC);
|
||||
|
||||
vkDestroyImageView(context.device, surfaceContext.depth.view, VKALLOC);
|
||||
vkDestroyImage(context.device, surfaceContext.depth.image, VKALLOC);
|
||||
vkFreeMemory(context.device, surfaceContext.depth.memory, VKALLOC);
|
||||
vkDestroyImageView(device, surfaceContext.depth.view, VKALLOC);
|
||||
vkDestroyImage(device, surfaceContext.depth.image, VKALLOC);
|
||||
vkFreeMemory(device, surfaceContext.depth.memory, VKALLOC);
|
||||
}
|
||||
|
||||
// makeSwapChainPresentable()
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
|
||||
private:
|
||||
struct Disposable {
|
||||
size_t refcount = 1;
|
||||
int refcount = 1;
|
||||
std::function<void()> destructor;
|
||||
};
|
||||
tsl::robin_map<Key, Disposable> mDisposables;
|
||||
|
||||
@@ -379,7 +379,7 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, size_t count)
|
||||
void VulkanDriver::createUniformBufferR(Handle<HwUniformBuffer> ubh, size_t size,
|
||||
BufferUsage usage) {
|
||||
auto uniformBuffer = construct_handle<VulkanUniformBuffer>(mHandleMap, ubh, mContext,
|
||||
mStagePool, size, usage);
|
||||
mStagePool, mDisposer, size, usage);
|
||||
mDisposer.createDisposable(uniformBuffer, [this, ubh] () {
|
||||
destruct_handle<VulkanUniformBuffer>(mHandleMap, ubh);
|
||||
});
|
||||
@@ -389,21 +389,26 @@ void VulkanDriver::destroyUniformBuffer(Handle<HwUniformBuffer> ubh) {
|
||||
if (ubh) {
|
||||
auto buffer = handle_cast<VulkanUniformBuffer>(mHandleMap, ubh);
|
||||
mBinder.unbindUniformBuffer(buffer->getGpuBuffer());
|
||||
|
||||
// We do not know if any pending draw calls are making use of this uniform buffer,
|
||||
// so assume the worst: that all command buffers are all using it.
|
||||
if (mContext.currentSurface) {
|
||||
for (auto& swapContext : mContext.currentSurface->swapContexts) {
|
||||
mDisposer.acquire(buffer, swapContext.commands.resources);
|
||||
}
|
||||
}
|
||||
|
||||
mDisposer.removeReference(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, int) {
|
||||
auto renderPrimitive = construct_handle<VulkanRenderPrimitive>(mHandleMap, rph, mContext);
|
||||
mDisposer.createDisposable(renderPrimitive, [this, rph] () {
|
||||
destruct_handle<VulkanRenderPrimitive>(mHandleMap, rph);
|
||||
});
|
||||
construct_handle<VulkanRenderPrimitive>(mHandleMap, rph, mContext);
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (rph) {
|
||||
auto renderPrimitive = handle_cast<VulkanRenderPrimitive>(mHandleMap, rph);
|
||||
mDisposer.removeReference(renderPrimitive);
|
||||
destruct_handle<VulkanRenderPrimitive>(mHandleMap, rph);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +416,7 @@ void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t buffe
|
||||
uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes,
|
||||
BufferUsage usage) {
|
||||
auto vertexBuffer = construct_handle<VulkanVertexBuffer>(mHandleMap, vbh, mContext, mStagePool,
|
||||
bufferCount, attributeCount, elementCount, attributes);
|
||||
mDisposer, bufferCount, attributeCount, elementCount, attributes);
|
||||
mDisposer.createDisposable(vertexBuffer, [this, vbh] () {
|
||||
destruct_handle<VulkanVertexBuffer>(mHandleMap, vbh);
|
||||
});
|
||||
@@ -428,7 +433,7 @@ void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh,
|
||||
ElementType elementType, uint32_t indexCount, BufferUsage usage) {
|
||||
auto elementSize = (uint8_t) getElementTypeSize(elementType);
|
||||
auto indexBuffer = construct_handle<VulkanIndexBuffer>(mHandleMap, ibh, mContext, mStagePool,
|
||||
elementSize, indexCount);
|
||||
mDisposer, elementSize, indexCount);
|
||||
mDisposer.createDisposable(indexBuffer, [this, ibh] () {
|
||||
destruct_handle<VulkanIndexBuffer>(mHandleMap, ibh);
|
||||
});
|
||||
@@ -554,7 +559,8 @@ void VulkanDriver::createSyncR(Handle<HwSync> sh, int) {
|
||||
|
||||
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
const VkInstance instance = mContext.instance;
|
||||
auto vksurface = (VkSurfaceKHR) mContextManager.createVkSurfaceKHR(nativeWindow, instance);
|
||||
auto vksurface = (VkSurfaceKHR) mContextManager.createVkSurfaceKHR(nativeWindow, instance,
|
||||
flags);
|
||||
auto* swapChain = construct_handle<VulkanSwapChain>(mHandleMap, sch, mContext, vksurface);
|
||||
|
||||
// TODO: move the following line into makeCurrent.
|
||||
@@ -1293,25 +1299,22 @@ void VulkanDriver::stopCapture(int) {
|
||||
|
||||
}
|
||||
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
uint32_t x, uint32_t y, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& pbd) {
|
||||
// TODO: add support for all types listed in the Renderer docstring for readPixels.
|
||||
assert(pbd.type == PixelBufferDescriptor::PixelDataType::UBYTE);
|
||||
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) {
|
||||
const VkDevice device = mContext.device;
|
||||
const VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget>(mHandleMap, src);
|
||||
const VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
|
||||
const VkFormat swapChainFormat = mContext.currentSurface->surfaceFormat.format;
|
||||
const VkFormat srcFormat = srcTexture ? srcTexture->vkformat : swapChainFormat;
|
||||
const bool swizzle = srcFormat == VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
// Create a host visible, linearly tiled image as a staging area.
|
||||
|
||||
VkImageCreateInfo imageInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.extent = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
},
|
||||
.format = srcFormat,
|
||||
.extent = { width, height, 1 },
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
@@ -1336,10 +1339,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
|
||||
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
|
||||
|
||||
// TODO: Should we allow readPixels within beginFrame / endFrame?
|
||||
|
||||
assert(mContext.currentCommands == nullptr);
|
||||
acquireWorkCommandBuffer(mContext);
|
||||
// TODO: replace waitForIdle with an image barrier coupled with acquireWorkCommandBuffer.
|
||||
waitForIdle(mContext);
|
||||
|
||||
// Transition the staging image layout.
|
||||
|
||||
@@ -1347,9 +1348,12 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, 1, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
|
||||
const uint8_t srcMipLevel = srcTarget->getColor(0).level;
|
||||
|
||||
VkImageCopy imageCopyRegion = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = srcMipLevel,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffset = {
|
||||
@@ -1369,11 +1373,10 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
|
||||
// Transition the source image layout (which might be the swap chain)
|
||||
|
||||
VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget>(mHandleMap, src);
|
||||
VkImage srcImage = srcTarget->getColor(0).image;
|
||||
VulkanTexture::transitionImageLayout(mContext.work.cmdbuffer, srcImage,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcMipLevel, 1, 1,
|
||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
|
||||
// Perform the blit.
|
||||
|
||||
@@ -1383,16 +1386,15 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
|
||||
// Restore the source image layout.
|
||||
|
||||
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
|
||||
if (srcTexture || mContext.currentSurface->presentQueue) {
|
||||
const VkImageLayout present = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
VulkanTexture::transitionImageLayout(mContext.work.cmdbuffer, srcImage,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, srcTexture ? getTextureLayout(srcTexture->usage) : present,
|
||||
0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
srcMipLevel, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
} else {
|
||||
VulkanTexture::transitionImageLayout(mContext.work.cmdbuffer, srcImage,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
|
||||
0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
srcMipLevel, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
// Transition the staging image layout to GENERAL.
|
||||
@@ -1418,63 +1420,36 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
vkCmdPipelineBarrier(mContext.work.cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
// Flush and wait.
|
||||
|
||||
flushWorkCommandBuffer(mContext);
|
||||
acquireWorkCommandBuffer(mContext);
|
||||
|
||||
// Create a closure-friendly pointer that holds the rvalue reference.
|
||||
VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
|
||||
VkSubresourceLayout subResourceLayout;
|
||||
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
|
||||
|
||||
PixelBufferDescriptor* closure = new PixelBufferDescriptor();
|
||||
*closure = std::move(pbd);
|
||||
// Map image memory so we can start copying from it.
|
||||
|
||||
// Create a disposable to defer execution of the following code until after
|
||||
// the work command buffer has completed.
|
||||
const uint8_t* srcPixels;
|
||||
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
|
||||
srcPixels += subResourceLayout.offset;
|
||||
|
||||
mDisposer.createDisposable((VulkanDisposer::Key) stagingImage, [=] () {
|
||||
// TODO: investigate why this Y-flip exists. This conditional seems to work with both
|
||||
// test_ReadPixels.cpp (readpixels from a normal render target with texture attachment) and
|
||||
// viewer_basic_test.cc (readpixels from an offscreen swap chain)
|
||||
const bool flipY = srcTexture ? true : false;
|
||||
|
||||
VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
|
||||
VkSubresourceLayout subResourceLayout;
|
||||
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
|
||||
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), srcPixels,
|
||||
subResourceLayout.rowPitch, width, height, swizzle, flipY)) {
|
||||
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
}
|
||||
|
||||
// Map image memory so we can start copying from it.
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
vkFreeMemory(device, stagingMemory, nullptr);
|
||||
vkDestroyImage(device, stagingImage, nullptr);
|
||||
|
||||
const uint8_t* srcPixels;
|
||||
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
|
||||
srcPixels += subResourceLayout.offset;
|
||||
|
||||
uint8_t* dstPixels = (uint8_t*) closure->buffer;
|
||||
const uint32_t dstStride = closure->stride ? closure->stride : width;
|
||||
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(closure->format,
|
||||
closure->type, dstStride, 1, closure->alignment);
|
||||
const int srcBytesPerRow = subResourceLayout.rowPitch;
|
||||
const VkFormat swapChainFormat = mContext.currentSurface->surfaceFormat.format;
|
||||
const bool swizzle = !srcTexture && swapChainFormat == VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
switch (closure->format) {
|
||||
case PixelDataFormat::RGB:
|
||||
case PixelDataFormat::RGB_INTEGER:
|
||||
DataReshaper::reshapeImage<uint8_t, 4, 3>(dstPixels, srcPixels, srcBytesPerRow,
|
||||
dstBytesPerRow, height, swizzle);
|
||||
break;
|
||||
case PixelDataFormat::RGBA:
|
||||
case PixelDataFormat::RGBA_INTEGER:
|
||||
DataReshaper::reshapeImage<uint8_t, 4, 4>(dstPixels, srcPixels, srcBytesPerRow,
|
||||
dstBytesPerRow, height, swizzle);
|
||||
break;
|
||||
default:
|
||||
utils::slog.e << "ReadPixels: invalid PixelDataFormat" << utils::io::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
vkFreeMemory(device, stagingMemory, nullptr);
|
||||
vkDestroyImage(device, stagingImage, nullptr);
|
||||
|
||||
scheduleDestroy(std::move(*closure));
|
||||
delete closure;
|
||||
});
|
||||
|
||||
// Next we reduce the ref count of the image to zero, which schedules the above callback to be
|
||||
// executed on the next beginFrame(), after the work command buffer is completed.
|
||||
mDisposer.removeReference((VulkanDisposer::Key) stagingImage);
|
||||
scheduleDestroy(std::move(pbd));
|
||||
}
|
||||
|
||||
void VulkanDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
|
||||
@@ -1600,6 +1575,8 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
|
||||
auto* program = handle_cast<VulkanProgram>(mHandleMap, programHandle);
|
||||
mDisposer.acquire(program, commands->resources);
|
||||
mDisposer.acquire(prim.indexBuffer, commands->resources);
|
||||
mDisposer.acquire(prim.vertexBuffer, commands->resources);
|
||||
|
||||
// If this is a debug build, validate the current shader.
|
||||
#if !defined(NDEBUG)
|
||||
|
||||
@@ -261,7 +261,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanContext& context, uint32_t width, uint32_
|
||||
|
||||
surfaceContext.swapContexts[i].attachment = {
|
||||
.format = surfaceContext.surfaceFormat.format, .image = image,
|
||||
.view = {}, .memory = {}, .texture = {}, .layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
.view = {}, .memory = imageMemory, .texture = {}, .layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
VkImageViewCreateInfo ivCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
@@ -396,8 +396,8 @@ bool VulkanRenderTarget::invalidate() {
|
||||
}
|
||||
|
||||
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
|
||||
AttributeArray const& attributes) :
|
||||
VulkanDisposer& disposer, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t elementCount, AttributeArray const& attributes) :
|
||||
HwVertexBuffer(bufferCount, attributeCount, elementCount, attributes) {
|
||||
buffers.reserve(bufferCount);
|
||||
for (uint8_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) {
|
||||
@@ -408,14 +408,14 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
|
||||
size = std::max(size, end);
|
||||
}
|
||||
}
|
||||
buffers.emplace_back(new VulkanBuffer(context, stagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
size));
|
||||
buffers.emplace_back(new VulkanBuffer(context, stagePool, disposer, this,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, size));
|
||||
}
|
||||
}
|
||||
|
||||
VulkanUniformBuffer::VulkanUniformBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
uint32_t numBytes, backend::BufferUsage usage)
|
||||
: mContext(context), mStagePool(stagePool) {
|
||||
VulkanDisposer& disposer, uint32_t numBytes, backend::BufferUsage usage)
|
||||
: mContext(context), mStagePool(stagePool), mDisposer(disposer) {
|
||||
// Create the VkBuffer.
|
||||
VkBufferCreateInfo bufferInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
@@ -439,6 +439,7 @@ void VulkanUniformBuffer::loadFromCpu(const void* cpuData, uint32_t numBytes) {
|
||||
auto copyToDevice = [this, numBytes, stage] (VulkanCommandBuffer& commands) {
|
||||
VkBufferCopy region { .size = numBytes };
|
||||
vkCmdCopyBuffer(commands.cmdbuffer, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
mDisposer.acquire(this, commands.resources);
|
||||
|
||||
// Ensure that the copy finishes before the next draw call.
|
||||
VkBufferMemoryBarrier barrier {
|
||||
|
||||
@@ -78,31 +78,32 @@ struct VulkanSwapChain : public HwSwapChain {
|
||||
};
|
||||
|
||||
struct VulkanVertexBuffer : public HwVertexBuffer {
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t elementCount,
|
||||
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer,
|
||||
uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
|
||||
AttributeArray const& attributes);
|
||||
std::vector<std::unique_ptr<VulkanBuffer>> buffers;
|
||||
};
|
||||
|
||||
struct VulkanIndexBuffer : public HwIndexBuffer {
|
||||
VulkanIndexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
VulkanIndexBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer,
|
||||
uint8_t elementSize, uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32),
|
||||
buffer(new VulkanBuffer(context, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
elementSize * indexCount)) {}
|
||||
buffer(new VulkanBuffer(context, stagePool, disposer, this,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount)) {}
|
||||
const VkIndexType indexType;
|
||||
const std::unique_ptr<VulkanBuffer> buffer;
|
||||
};
|
||||
|
||||
struct VulkanUniformBuffer : public HwUniformBuffer {
|
||||
VulkanUniformBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint32_t numBytes,
|
||||
backend::BufferUsage usage);
|
||||
VulkanUniformBuffer(VulkanContext& context, VulkanStagePool& stagePool,
|
||||
VulkanDisposer& disposer, uint32_t numBytes, backend::BufferUsage usage);
|
||||
~VulkanUniformBuffer();
|
||||
void loadFromCpu(const void* cpuData, uint32_t numBytes);
|
||||
VkBuffer getGpuBuffer() const { return mGpuBuffer; }
|
||||
private:
|
||||
VulkanContext& mContext;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanDisposer& mDisposer;
|
||||
VkBuffer mGpuBuffer;
|
||||
VmaAllocation mGpuMemory;
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace backend {
|
||||
class VulkanPlatform : public DefaultPlatform {
|
||||
public:
|
||||
// Given a Vulkan instance and native window handle, creates the platform-specific surface.
|
||||
virtual void* createVkSurfaceKHR(void* nativeWindow, void* instance) noexcept = 0;
|
||||
virtual void* createVkSurfaceKHR(void* nativeWindow, void* instance, uint64_t flags) noexcept = 0;
|
||||
|
||||
~VulkanPlatform() override;
|
||||
};
|
||||
|
||||
@@ -281,5 +281,101 @@ VkFrontFace getFrontFace(bool inverseFrontFaces) {
|
||||
VkFrontFace::VK_FRONT_FACE_CLOCKWISE : VkFrontFace::VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
}
|
||||
|
||||
PixelDataType getComponentType(VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
case VK_FORMAT_R8_SNORM:
|
||||
case VK_FORMAT_R8_USCALED:
|
||||
case VK_FORMAT_R8_SSCALED:
|
||||
case VK_FORMAT_R8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_R8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
case VK_FORMAT_R8G8_SNORM:
|
||||
case VK_FORMAT_R8G8_USCALED:
|
||||
case VK_FORMAT_R8G8_SSCALED:
|
||||
case VK_FORMAT_R8G8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_R8G8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
case VK_FORMAT_R8G8B8_UNORM:
|
||||
case VK_FORMAT_R8G8B8_SNORM:
|
||||
case VK_FORMAT_R8G8B8_USCALED:
|
||||
case VK_FORMAT_R8G8B8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_R8G8B8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
case VK_FORMAT_B8G8R8_UNORM: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_B8G8R8_SNORM: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_B8G8R8_USCALED:
|
||||
case VK_FORMAT_B8G8R8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_B8G8R8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_R8G8B8A8_SNORM:
|
||||
case VK_FORMAT_R8G8B8A8_USCALED:
|
||||
case VK_FORMAT_R8G8B8A8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8A8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_R8G8B8A8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SNORM:
|
||||
case VK_FORMAT_B8G8R8A8_USCALED:
|
||||
case VK_FORMAT_B8G8R8A8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8A8_UINT: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_B8G8R8A8_SINT: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_UINT_PACK32: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_A8B8G8R8_SINT_PACK32: return PixelDataType::BYTE;
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32: return PixelDataType::UBYTE;
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
case VK_FORMAT_R16_SNORM:
|
||||
case VK_FORMAT_R16_USCALED:
|
||||
case VK_FORMAT_R16_SSCALED:
|
||||
case VK_FORMAT_R16_UINT: return PixelDataType::USHORT;
|
||||
case VK_FORMAT_R16_SINT: return PixelDataType::SHORT;
|
||||
case VK_FORMAT_R16_SFLOAT: return PixelDataType::HALF;
|
||||
case VK_FORMAT_R16G16_UNORM:
|
||||
case VK_FORMAT_R16G16_SNORM:
|
||||
case VK_FORMAT_R16G16_USCALED:
|
||||
case VK_FORMAT_R16G16_SSCALED:
|
||||
case VK_FORMAT_R16G16_UINT: return PixelDataType::USHORT;
|
||||
case VK_FORMAT_R16G16_SINT: return PixelDataType::SHORT;
|
||||
case VK_FORMAT_R16G16_SFLOAT: return PixelDataType::HALF;
|
||||
case VK_FORMAT_R16G16B16_UNORM:
|
||||
case VK_FORMAT_R16G16B16_SNORM:
|
||||
case VK_FORMAT_R16G16B16_USCALED:
|
||||
case VK_FORMAT_R16G16B16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16_UINT: return PixelDataType::USHORT;
|
||||
case VK_FORMAT_R16G16B16_SINT: return PixelDataType::SHORT;
|
||||
case VK_FORMAT_R16G16B16_SFLOAT: return PixelDataType::HALF;
|
||||
case VK_FORMAT_R16G16B16A16_UNORM:
|
||||
case VK_FORMAT_R16G16B16A16_SNORM:
|
||||
case VK_FORMAT_R16G16B16A16_USCALED:
|
||||
case VK_FORMAT_R16G16B16A16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16A16_UINT: return PixelDataType::USHORT;
|
||||
case VK_FORMAT_R16G16B16A16_SINT: return PixelDataType::SHORT;
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT: return PixelDataType::HALF;
|
||||
case VK_FORMAT_R32_UINT: return PixelDataType::UINT;
|
||||
case VK_FORMAT_R32_SINT: return PixelDataType::INT;
|
||||
case VK_FORMAT_R32_SFLOAT: return PixelDataType::FLOAT;
|
||||
case VK_FORMAT_R32G32_UINT: return PixelDataType::UINT;
|
||||
case VK_FORMAT_R32G32_SINT: return PixelDataType::INT;
|
||||
case VK_FORMAT_R32G32_SFLOAT: return PixelDataType::FLOAT;
|
||||
case VK_FORMAT_R32G32B32_UINT: return PixelDataType::UINT;
|
||||
case VK_FORMAT_R32G32B32_SINT: return PixelDataType::INT;
|
||||
case VK_FORMAT_R32G32B32_SFLOAT: return PixelDataType::FLOAT;
|
||||
case VK_FORMAT_R32G32B32A32_UINT: return PixelDataType::UINT;
|
||||
case VK_FORMAT_R32G32B32A32_SINT: return PixelDataType::INT;
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT: return PixelDataType::FLOAT;
|
||||
default: assert(false && "Unknown data type, conversion is not supported.");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
} // namespace backend
|
||||
|
||||
@@ -32,6 +32,7 @@ VkCompareOp getCompareOp(SamplerCompareFunc func);
|
||||
VkBlendFactor getBlendFactor(BlendFunction mode);
|
||||
VkCullModeFlags getCullMode(CullingMode mode);
|
||||
VkFrontFace getFrontFace(bool inverseFrontFaces);
|
||||
PixelDataType getComponentType(VkFormat format);
|
||||
|
||||
} // namespace filament
|
||||
} // namespace backend
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
#ifndef IOS
|
||||
#include <imageio/ImageEncoder.h>
|
||||
#include <image/ColorTransform.h>
|
||||
|
||||
using namespace image;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -52,9 +62,6 @@ void main() {
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST_F(BackendTest, ReadPixels) {
|
||||
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
|
||||
// asserting an exact pixel-for-pixel match. So far, rendering on macOS and iPhone have had
|
||||
@@ -117,6 +124,23 @@ TEST_F(BackendTest, ReadPixels) {
|
||||
return bufferDimension;
|
||||
}
|
||||
|
||||
void exportScreenshot(void* pixelData) const {
|
||||
#ifndef IOS
|
||||
const size_t width = readRect.width, height = readRect.height;
|
||||
LinearImage image(width, height, 4);
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
||||
image = toLinear<uint8_t>(width, height, width * 4, (uint8_t*) pixelData);
|
||||
}
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
||||
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
||||
}
|
||||
std::string png = std::string(testName) + ".png";
|
||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
||||
png.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
PixelDataFormat format = PixelDataFormat::RGBA;
|
||||
PixelDataType type = PixelDataType::UBYTE;
|
||||
};
|
||||
@@ -256,6 +280,8 @@ TEST_F(BackendTest, ReadPixels) {
|
||||
const TestCase* test = (const TestCase*) user;
|
||||
assert(test);
|
||||
|
||||
test->exportScreenshot(buffer);
|
||||
|
||||
// Hash the contents of the buffer and check that they match.
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
||||
|
||||
@@ -264,6 +290,7 @@ TEST_F(BackendTest, ReadPixels) {
|
||||
|
||||
free(buffer);
|
||||
}, (void*) &t);
|
||||
|
||||
getDriverApi().readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
|
||||
t.readRect.height, std::move(descriptor));
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ public:
|
||||
*/
|
||||
static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
|
||||
|
||||
void* getNativeWindow() const noexcept;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,26 @@ material {
|
||||
{
|
||||
type : float4,
|
||||
name : vignetteColor
|
||||
},
|
||||
{
|
||||
type : subpassInput,
|
||||
format : float,
|
||||
precision : medium,
|
||||
name : colorBuffer,
|
||||
}
|
||||
],
|
||||
outputs : [
|
||||
{
|
||||
name : color,
|
||||
target : color,
|
||||
type : float4,
|
||||
location : 0
|
||||
},
|
||||
{
|
||||
name : tonemappedOutput,
|
||||
target : color,
|
||||
type : float4,
|
||||
location : 1
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
@@ -45,11 +65,6 @@ vertex {
|
||||
|
||||
fragment {
|
||||
|
||||
// TODO: this should be specified as a parameter
|
||||
// In our Vulkan backend, subpass inputs always live in descriptor set 2. (ignored for GLES)
|
||||
layout (input_attachment_index = 0, set = 2, binding = 0) uniform mediump subpassInput colorBuffer;
|
||||
layout(location = 1) out vec4 tonemappedOutput;
|
||||
|
||||
#include "../../../../shaders/src/dithering.fs"
|
||||
#include "../../../../shaders/src/vignette.fs"
|
||||
|
||||
@@ -64,11 +79,11 @@ fragment {
|
||||
}
|
||||
|
||||
vec3 resolveFragment(const ivec2 uv) {
|
||||
return subpassLoad(colorBuffer).rgb;
|
||||
return subpassLoad(materialParams_colorBuffer).rgb;
|
||||
}
|
||||
|
||||
vec4 resolveAlphaFragment(const ivec2 uv) {
|
||||
return subpassLoad(colorBuffer);
|
||||
return subpassLoad(materialParams_colorBuffer);
|
||||
}
|
||||
|
||||
vec4 resolve() {
|
||||
@@ -104,8 +119,8 @@ fragment {
|
||||
#else
|
||||
postProcess.color = dithered;
|
||||
#endif
|
||||
postProcess.tonemappedOutput = postProcess.color;
|
||||
}
|
||||
tonemappedOutput = postProcess.color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.9.6"
|
||||
spec.version = "1.9.7"
|
||||
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.9.6/filament-v1.9.6-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.9.7/filament-v1.9.7-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,8 @@
|
||||
#elif defined(__linux__)
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#define VK_USE_PLATFORM_XCB_KHR 1
|
||||
#else
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#define VK_USE_PLATFORM_XLIB_KHR 1
|
||||
#endif
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
@@ -40,6 +40,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
Unknown = charTo64bitNum("UNKNOWN "),
|
||||
MaterialUib = charTo64bitNum("MAT_UIB "),
|
||||
MaterialSib = charTo64bitNum("MAT_SIB "),
|
||||
MaterialSubpass = charTo64bitNum("MAT_SUB "),
|
||||
MaterialGlsl = charTo64bitNum("MAT_GLSL"),
|
||||
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
|
||||
MaterialMetal = charTo64bitNum("MAT_METL"),
|
||||
|
||||
51
libs/filabridge/include/private/filament/SubpassInfo.h
Normal file
51
libs/filabridge/include/private/filament/SubpassInfo.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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_SUBPASSINFO_H
|
||||
#define TNT_FILAMENT_SUBPASSINFO_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using Type = backend::SubpassType;
|
||||
using Format = backend::SamplerFormat;
|
||||
using Precision = backend::Precision;
|
||||
|
||||
struct SubpassInfo {
|
||||
SubpassInfo() = default;
|
||||
SubpassInfo(utils::CString block, utils::CString name, Type type, Format format,
|
||||
Precision precision, uint8_t attachmentIndex, uint8_t binding) noexcept
|
||||
: block(std::move(block)), name(std::move(name)), type(type), format(format),
|
||||
precision(precision), attachmentIndex(attachmentIndex), binding(binding),
|
||||
isValid(true) {
|
||||
}
|
||||
// name of the block this subpass belongs to
|
||||
utils::CString block = utils::CString("MaterialParams");
|
||||
utils::CString name; // name of this subpass
|
||||
Type type; // type of this subpass
|
||||
Format format; // format of this subpass
|
||||
Precision precision; // precision of this subpass
|
||||
uint8_t attachmentIndex = 0;
|
||||
uint8_t binding = 0;
|
||||
bool isValid = false;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_SUBPASSINFO_H
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
struct ImDrawData;
|
||||
struct ImGuiIO;
|
||||
struct ImGuiContext;
|
||||
|
||||
namespace filagui {
|
||||
|
||||
@@ -87,6 +88,7 @@ public:
|
||||
utils::Entity mRenderable;
|
||||
filament::Texture* mTexture = nullptr;
|
||||
bool mHasSynced = false;
|
||||
ImGuiContext* mImGuiContext;
|
||||
};
|
||||
|
||||
} // namespace filagui
|
||||
|
||||
@@ -43,8 +43,8 @@ namespace filagui {
|
||||
#include "generated/resources/filagui_resources.h"
|
||||
|
||||
ImGuiHelper::ImGuiHelper(Engine* engine, filament::View* view, const Path& fontPath) :
|
||||
mEngine(engine), mView(view), mScene(engine->createScene()) {
|
||||
ImGui::CreateContext();
|
||||
mEngine(engine), mView(view), mScene(engine->createScene()),
|
||||
mImGuiContext(ImGui::CreateContext()) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Create a simple alpha-blended 2D blitting material.
|
||||
@@ -111,7 +111,8 @@ ImGuiHelper::~ImGuiHelper() {
|
||||
for (auto& ib : mIndexBuffers) {
|
||||
mEngine->destroy(ib);
|
||||
}
|
||||
ImGui::DestroyContext();
|
||||
ImGui::DestroyContext(mImGuiContext);
|
||||
mImGuiContext = nullptr;
|
||||
}
|
||||
|
||||
void ImGuiHelper::setDisplaySize(int width, int height, float scaleX, float scaleY) {
|
||||
@@ -122,6 +123,7 @@ void ImGuiHelper::setDisplaySize(int width, int height, float scaleX, float scal
|
||||
}
|
||||
|
||||
void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
||||
ImGui::SetCurrentContext(mImGuiContext);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DeltaTime = timeStepInSeconds;
|
||||
// First, let ImGui process events and increment its internal frame count.
|
||||
@@ -137,6 +139,8 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
||||
}
|
||||
|
||||
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
||||
ImGui::SetCurrentContext(mImGuiContext);
|
||||
|
||||
mHasSynced = false;
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace filamat {
|
||||
using Property = MaterialBuilder::Property;
|
||||
using UniformType = MaterialBuilder::UniformType;
|
||||
using SamplerType = MaterialBuilder::SamplerType;
|
||||
using SubpassType = MaterialBuilder::SubpassType;
|
||||
using SamplerFormat = MaterialBuilder::SamplerFormat;
|
||||
using SamplerPrecision = MaterialBuilder::SamplerPrecision;
|
||||
using OutputTarget = MaterialBuilder::OutputTarget;
|
||||
@@ -70,6 +71,7 @@ private:
|
||||
static std::unordered_map<std::string, Property> mStringToProperty;
|
||||
static std::unordered_map<std::string, UniformType> mStringToUniformType;
|
||||
static std::unordered_map<std::string, SamplerType> mStringToSamplerType;
|
||||
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
|
||||
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
|
||||
static std::unordered_map<std::string, SamplerPrecision> mStringToSamplerPrecision;
|
||||
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
|
||||
|
||||
@@ -189,6 +189,7 @@ public:
|
||||
|
||||
using UniformType = filament::backend::UniformType;
|
||||
using SamplerType = filament::backend::SamplerType;
|
||||
using SubpassType = filament::backend::SubpassType;
|
||||
using SamplerFormat = filament::backend::SamplerFormat;
|
||||
using SamplerPrecision = filament::backend::Precision;
|
||||
using CullingMode = filament::backend::CullingMode;
|
||||
@@ -469,7 +470,7 @@ public:
|
||||
|
||||
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
|
||||
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target,
|
||||
OutputType type, const char* name) noexcept;
|
||||
OutputType type, const char* name, int location = -1) noexcept;
|
||||
|
||||
MaterialBuilder& enableFramebufferFetch() noexcept;
|
||||
|
||||
@@ -481,36 +482,64 @@ public:
|
||||
// The methods and types below are for internal use
|
||||
/// @cond never
|
||||
|
||||
/**
|
||||
* Add a subpass parameter to this material.
|
||||
*/
|
||||
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, SamplerPrecision
|
||||
precision, const char* name) noexcept;
|
||||
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name)
|
||||
noexcept;
|
||||
MaterialBuilder& parameter(SubpassType subpassType, SamplerPrecision precision,
|
||||
const char* name) noexcept;
|
||||
MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept;
|
||||
|
||||
struct Parameter {
|
||||
Parameter() noexcept = default;
|
||||
Parameter() noexcept : parameterType(INVALID) {}
|
||||
Parameter(const char* paramName, SamplerType t, SamplerFormat f, SamplerPrecision p)
|
||||
: name(paramName), size(1), samplerType(t), samplerFormat(f), samplerPrecision(p),
|
||||
isSampler(true) { }
|
||||
: name(paramName), size(1), samplerType(t), format(f), precision(p),
|
||||
parameterType(SAMPLER) { }
|
||||
Parameter(const char* paramName, UniformType t, size_t typeSize)
|
||||
: name(paramName), size(typeSize), uniformType(t), isSampler(false) { }
|
||||
: name(paramName), size(typeSize), uniformType(t), parameterType(UNIFORM) { }
|
||||
Parameter(const char* paramName, SubpassType t, SamplerFormat f, SamplerPrecision p)
|
||||
: name(paramName), size(1), subpassType(t), format(f), precision(p),
|
||||
parameterType(SUBPASS) { }
|
||||
utils::CString name;
|
||||
size_t size;
|
||||
union {
|
||||
UniformType uniformType;
|
||||
struct {
|
||||
SamplerType samplerType;
|
||||
SamplerFormat samplerFormat;
|
||||
SamplerPrecision samplerPrecision;
|
||||
union {
|
||||
SamplerType samplerType;
|
||||
SubpassType subpassType;
|
||||
};
|
||||
SamplerFormat format;
|
||||
SamplerPrecision precision;
|
||||
};
|
||||
};
|
||||
bool isSampler;
|
||||
enum {
|
||||
INVALID,
|
||||
UNIFORM,
|
||||
SAMPLER,
|
||||
SUBPASS
|
||||
} parameterType;
|
||||
|
||||
bool isSampler() const { return parameterType == SAMPLER; }
|
||||
bool isUniform() const { return parameterType == UNIFORM; }
|
||||
bool isSubpass() const { return parameterType == SUBPASS; }
|
||||
};
|
||||
|
||||
struct Output {
|
||||
Output() noexcept = default;
|
||||
Output(const char* outputName, VariableQualifier qualifier, OutputTarget target,
|
||||
OutputType type)
|
||||
: name(outputName), qualifier(qualifier), target(target), type(type) { }
|
||||
OutputType type, int location) noexcept
|
||||
: name(outputName), qualifier(qualifier), target(target), type(type),
|
||||
location(location) { }
|
||||
|
||||
utils::CString name;
|
||||
VariableQualifier qualifier;
|
||||
OutputTarget target;
|
||||
OutputType type;
|
||||
int location;
|
||||
};
|
||||
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
|
||||
@@ -535,6 +564,7 @@ public:
|
||||
bool hasExternalSampler() const noexcept;
|
||||
|
||||
static constexpr size_t MAX_PARAMETERS_COUNT = 48;
|
||||
static constexpr size_t MAX_SUBPASS_COUNT = 1;
|
||||
using ParameterList = Parameter[MAX_PARAMETERS_COUNT];
|
||||
|
||||
// returns the number of parameters declared in this material
|
||||
|
||||
@@ -94,6 +94,15 @@ std::unordered_map<std::string, SamplerType>& Enums::getMap<SamplerType>() noexc
|
||||
return mStringToSamplerType;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, SubpassType> Enums::mStringToSubpassType = {
|
||||
{ "subpassInput", SubpassType::SUBPASS_INPUT },
|
||||
};
|
||||
|
||||
template <>
|
||||
std::unordered_map<std::string, SubpassType>& Enums::getMap<SubpassType>() noexcept {
|
||||
return mStringToSubpassType;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, SamplerPrecision> Enums::mStringToSamplerPrecision = {
|
||||
{ "default", SamplerPrecision::DEFAULT },
|
||||
{ "low", SamplerPrecision::LOW },
|
||||
|
||||
@@ -196,6 +196,20 @@ MaterialBuilder& MaterialBuilder::parameter(
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, SamplerFormat format,
|
||||
SamplerPrecision precision, const char* name) noexcept {
|
||||
ASSERT_PRECONDITION(format == SamplerFormat::FLOAT,
|
||||
"Subpass parameters must have FLOAT format.");
|
||||
|
||||
auto subpassCount = std::count_if(std::begin(mParameters), std::end(mParameters),
|
||||
[](const auto& p) { return p.isSubpass(); });
|
||||
|
||||
ASSERT_POSTCONDITION(subpassCount < MAX_SUBPASS_COUNT, "Too many subpasses");
|
||||
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
|
||||
mParameters[mParameterCount++] = { name, subpassType, format, precision };
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::parameter(
|
||||
SamplerType samplerType, SamplerFormat format, const char* name) noexcept {
|
||||
return parameter(samplerType, format, SamplerPrecision::DEFAULT, name);
|
||||
@@ -211,6 +225,20 @@ MaterialBuilder& MaterialBuilder::parameter(
|
||||
return parameter(samplerType, SamplerFormat::FLOAT, SamplerPrecision::DEFAULT, name);
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, SamplerFormat format,
|
||||
const char* name) noexcept {
|
||||
return parameter(subpassType, format, SamplerPrecision::DEFAULT, name);
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, SamplerPrecision precision,
|
||||
const char* name) noexcept {
|
||||
return parameter(subpassType, SamplerFormat::FLOAT, precision, name);
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, const char* name) noexcept {
|
||||
return parameter(subpassType, SamplerFormat::FLOAT, SamplerPrecision::DEFAULT, name);
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::require(filament::VertexAttribute attribute) noexcept {
|
||||
mRequiredAttributes.set(attribute);
|
||||
return *this;
|
||||
@@ -363,7 +391,7 @@ MaterialBuilder& MaterialBuilder::shaderDefine(const char* name, const char* val
|
||||
bool MaterialBuilder::hasExternalSampler() const noexcept {
|
||||
for (size_t i = 0, c = mParameterCount; i < c; i++) {
|
||||
auto const& param = mParameters[i];
|
||||
if (param.isSampler && param.samplerType == SamplerType::SAMPLER_EXTERNAL) {
|
||||
if (param.isSampler() && param.samplerType == SamplerType::SAMPLER_EXTERNAL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -378,10 +406,17 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
|
||||
filament::UniformInterfaceBlock::Builder ibb;
|
||||
for (size_t i = 0, c = mParameterCount; i < c; i++) {
|
||||
auto const& param = mParameters[i];
|
||||
if (param.isSampler) {
|
||||
sbb.add(param.name, param.samplerType, param.samplerFormat, param.samplerPrecision);
|
||||
} else {
|
||||
if (param.isSampler()) {
|
||||
sbb.add(param.name, param.samplerType, param.format, param.precision);
|
||||
} else if (param.isUniform()) {
|
||||
ibb.add(param.name, param.size, param.uniformType);
|
||||
} else if (param.isSubpass()) {
|
||||
// For now, we only support a single subpass for attachment 0.
|
||||
// Subpasses blong to the "MaterialParams" block.
|
||||
const uint8_t attachmentIndex = 0;
|
||||
const uint8_t binding = 0;
|
||||
info.subpass = { utils::CString("MaterialParams"), param.name, param.subpassType,
|
||||
param.format, param.precision, attachmentIndex, binding };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,15 +761,24 @@ bool MaterialBuilder::generateShaders(const std::vector<Variant>& variants, Chun
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::output(VariableQualifier qualifier, OutputTarget target,
|
||||
OutputType type, const char* name) noexcept {
|
||||
OutputType type, const char* name, int location) noexcept {
|
||||
|
||||
ASSERT_PRECONDITION(target != OutputTarget::DEPTH || type == OutputType::FLOAT,
|
||||
"Depth outputs must be of type FLOAT.");
|
||||
ASSERT_PRECONDITION(target != OutputTarget::DEPTH || qualifier == VariableQualifier::OUT,
|
||||
"Depth outputs must use OUT qualifier.");
|
||||
|
||||
ASSERT_PRECONDITION(location >= -1,
|
||||
"Output location must be >= 0 (or use -1 for default location).");
|
||||
|
||||
// A location value of -1 signals using the default location. We'll simply take the previous
|
||||
// output's location and add 1.
|
||||
if (location == -1) {
|
||||
location = mOutputs.empty() ? 0 : mOutputs.back().location + 1;
|
||||
}
|
||||
|
||||
// Unconditionally add this output, then we'll check if we've maxed on on any particular target.
|
||||
mOutputs.emplace_back(name, qualifier, target, type);
|
||||
auto& output = mOutputs.emplace_back(name, qualifier, target, type, location);
|
||||
|
||||
uint8_t colorOutputCount = 0;
|
||||
uint8_t depthOutputCount = 0;
|
||||
@@ -864,6 +908,9 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
|
||||
// SIB
|
||||
container.addChild<MaterialSamplerInterfaceBlockChunk>(info.sib);
|
||||
|
||||
// Subpass
|
||||
container.addChild<MaterialSubpassInterfaceBlockChunk>(info.subpass);
|
||||
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSidedSet, mDoubleSidedCapability);
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialDoubleSided, mDoubleSided);
|
||||
|
||||
|
||||
@@ -56,4 +56,22 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
}
|
||||
}
|
||||
|
||||
MaterialSubpassInterfaceBlockChunk::MaterialSubpassInterfaceBlockChunk(SubpassInfo& subpass) :
|
||||
Chunk(ChunkType::MaterialSubpass),
|
||||
mSubpass(subpass) {
|
||||
}
|
||||
|
||||
void MaterialSubpassInterfaceBlockChunk::flatten(Flattener &f) {
|
||||
f.writeString(mSubpass.block.c_str());
|
||||
f.writeUint64(mSubpass.isValid ? 1 : 0); // only ever a single subpass for now
|
||||
if (mSubpass.isValid) {
|
||||
f.writeString(mSubpass.name.c_str());
|
||||
f.writeUint8(static_cast<uint8_t>(mSubpass.type));
|
||||
f.writeUint8(static_cast<uint8_t>(mSubpass.format));
|
||||
f.writeUint8(static_cast<uint8_t>(mSubpass.precision));
|
||||
f.writeUint8(static_cast<uint8_t>(mSubpass.attachmentIndex));
|
||||
f.writeUint8(static_cast<uint8_t>(mSubpass.binding));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
namespace filamat {
|
||||
|
||||
@@ -46,6 +47,17 @@ private:
|
||||
filament::SamplerInterfaceBlock& mSib;
|
||||
};
|
||||
|
||||
class MaterialSubpassInterfaceBlockChunk final : public Chunk {
|
||||
public:
|
||||
explicit MaterialSubpassInterfaceBlockChunk(filament::SubpassInfo& subpass);
|
||||
~MaterialSubpassInterfaceBlockChunk() = default;
|
||||
|
||||
private:
|
||||
void flatten(Flattener &) override;
|
||||
|
||||
filament::SubpassInfo& mSubpass;
|
||||
};
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
#endif // TNT_FILAMAT_MAT_INTEFFACE_BLOCK_CHUNK_H
|
||||
|
||||
@@ -339,6 +339,29 @@ io::sstream& CodeGenerator::generateSamplers(
|
||||
return out;
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generateSubpass(utils::io::sstream& out,
|
||||
SubpassInfo subpass) const {
|
||||
if (!subpass.isValid) {
|
||||
return out;
|
||||
}
|
||||
|
||||
CString subpassName =
|
||||
SamplerInterfaceBlock::getUniformName(subpass.block.c_str(), subpass.name.c_str());
|
||||
|
||||
char const* const typeName = "subpassInput";
|
||||
// In our Vulkan backend, subpass inputs always live in descriptor set 2. (ignored for GLES)
|
||||
char const* const precision = getPrecisionQualifier(subpass.precision, Precision::DEFAULT);
|
||||
out << "layout(input_attachment_index = " << (int) subpass.attachmentIndex
|
||||
<< ", set = 2, binding = " << (int) subpass.binding
|
||||
<< ") ";
|
||||
out << "uniform " << precision << " " << typeName << " " << subpassName.c_str();
|
||||
out << ";\n";
|
||||
|
||||
out << "\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void CodeGenerator::fixupExternalSamplers(
|
||||
std::string& shader, SamplerInterfaceBlock const& sib) noexcept {
|
||||
auto const& infos = sib.getSamplerInfoList();
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
#include <utils/sstream.h>
|
||||
|
||||
@@ -105,6 +106,10 @@ public:
|
||||
utils::io::sstream& generateSamplers(
|
||||
utils::io::sstream& out, uint8_t firstBinding, const filament::SamplerInterfaceBlock& sib) const;
|
||||
|
||||
// generate subpass
|
||||
utils::io::sstream& generateSubpass(utils::io::sstream& out,
|
||||
filament::SubpassInfo subpass) const;
|
||||
|
||||
// generate material properties getters
|
||||
utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
|
||||
MaterialBuilder::Property property, bool isSet) const;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
#include <private/filament/SamplerBindingMap.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
@@ -51,6 +52,7 @@ struct UTILS_PUBLIC MaterialInfo {
|
||||
filament::Shading shading;
|
||||
filament::UniformInterfaceBlock uib;
|
||||
filament::SamplerInterfaceBlock sib;
|
||||
filament::SubpassInfo subpass;
|
||||
filament::SamplerBindingMap samplerBindings;
|
||||
};
|
||||
|
||||
|
||||
@@ -535,14 +535,16 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
// subpass
|
||||
cg.generateSubpass(fs, material.subpass);
|
||||
|
||||
cg.generateCommon(fs, ShaderType::FRAGMENT);
|
||||
cg.generatePostProcessGetters(fs, ShaderType::FRAGMENT);
|
||||
|
||||
// Generate post-process outputs.
|
||||
size_t outputIndex = 0;
|
||||
for (const auto& output : mOutputs) {
|
||||
if (output.target == MaterialBuilder::OutputTarget::COLOR) {
|
||||
cg.generateOutput(fs, ShaderType::FRAGMENT, output.name, outputIndex++,
|
||||
cg.generateOutput(fs, ShaderType::FRAGMENT, output.name, output.location,
|
||||
output.qualifier, output.type);
|
||||
}
|
||||
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
|
||||
|
||||
@@ -346,6 +346,21 @@ inline LinearImage fromLinearToRGBM(const LinearImage& image) {
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) {
|
||||
LinearImage result(w, h, 4);
|
||||
filament::math::float4* d = reinterpret_cast<filament::math::float4*>(result.getPixelRef(0, 0));
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
T const* p = reinterpret_cast<T const*>(src + y * bpr);
|
||||
for (size_t x = 0; x < w; ++x, p += 4) {
|
||||
filament::math::float3 sRGB(p[0], p[1], p[2]);
|
||||
sRGB /= std::numeric_limits<T>::max();
|
||||
*d++ = filament::math::float4(sRGBToLinear(sRGB), 1.0f);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Image
|
||||
|
||||
#endif // IMAGE_COLORTRANSFORM_H_
|
||||
|
||||
@@ -196,6 +196,13 @@ const char* toString(backend::SamplerType type) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
const char* toString(backend::SubpassType type) noexcept {
|
||||
switch (type) {
|
||||
case backend::SubpassType::SUBPASS_INPUT: return "subpassInput";
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
const char* toString(backend::Precision precision) noexcept {
|
||||
switch (precision) {
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace filament {
|
||||
namespace matdbg {
|
||||
|
||||
constexpr int alignment = 32;
|
||||
constexpr int shortAlignment = 12;
|
||||
constexpr int shortAlignment = 15;
|
||||
|
||||
static string arraySizeToString(uint64_t size) {
|
||||
if (size > 1) {
|
||||
@@ -249,6 +249,60 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
<< endl;
|
||||
}
|
||||
|
||||
// Subpasses are optional.
|
||||
if (container.hasChunk(ChunkType::MaterialSubpass)) {
|
||||
Unflattener subpasses(
|
||||
container.getChunkStart(ChunkType::MaterialSubpass),
|
||||
container.getChunkEnd(ChunkType::MaterialSubpass));
|
||||
|
||||
CString name;
|
||||
if (!subpasses.read(&name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t subpassCount;
|
||||
subpasses.read(&subpassCount);
|
||||
|
||||
for (uint64_t i = 0; i < subpassCount; i++) {
|
||||
CString fieldName;
|
||||
uint8_t fieldType;
|
||||
uint8_t fieldFormat;
|
||||
uint8_t fieldPrecision;
|
||||
uint8_t attachmentIndex;
|
||||
uint8_t binding;
|
||||
|
||||
if (!subpasses.read(&fieldName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subpasses.read(&fieldType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subpasses.read(&fieldFormat))
|
||||
return false;
|
||||
|
||||
if (!subpasses.read(&fieldPrecision)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subpasses.read(&attachmentIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subpasses.read(&binding)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
text << " "
|
||||
<< setw(alignment) << fieldName.c_str()
|
||||
<< setw(shortAlignment) << toString(SubpassType(fieldType))
|
||||
<< setw(shortAlignment) << toString(Precision(fieldPrecision))
|
||||
<< toString(SamplerFormat(fieldFormat))
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
text << endl;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -40,21 +40,6 @@ namespace viewer {
|
||||
|
||||
static std::string gStatus;
|
||||
|
||||
template<typename T>
|
||||
static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) {
|
||||
LinearImage result(w, h, 3);
|
||||
filament::math::float3* d = reinterpret_cast<filament::math::float3*>(result.getPixelRef(0, 0));
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
T const* p = reinterpret_cast<T const*>(src + y * bpr);
|
||||
for (size_t x = 0; x < w; ++x, p += 3) {
|
||||
filament::math::float3 sRGB(p[0], p[1], p[2]);
|
||||
sRGB /= std::numeric_limits<T>::max();
|
||||
*d++ = sRGBToLinear(sRGB);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ScreenshotState {
|
||||
View* view;
|
||||
std::string filename;
|
||||
@@ -162,10 +147,8 @@ void AutomationEngine::tick(View* view, MaterialInstance* const* materials, size
|
||||
|
||||
const int digits = (int) log10 ((double) mSpec->size()) + 1;
|
||||
std::ostringstream stringStream;
|
||||
stringStream << "test"
|
||||
<< std::setfill('0') << std::setw(digits)
|
||||
<< std::to_string(mCurrentTest) << "_"
|
||||
<< mSpec->getName(mCurrentTest);
|
||||
stringStream << mSpec->getName(mCurrentTest)
|
||||
<< std::setfill('0') << std::setw(digits) << mCurrentTest;
|
||||
std::string prefix = stringStream.str();
|
||||
|
||||
if (mOptions.exportSettings) {
|
||||
|
||||
@@ -48,7 +48,7 @@ static const char* DEFAULT_AUTOMATION = R"TXT([
|
||||
"name": "viewopts",
|
||||
"base": {
|
||||
"view.dof.focusDistance": 0.1
|
||||
}
|
||||
},
|
||||
"permute": {
|
||||
"view.sampleCount": [1, 4],
|
||||
"view.taa.enabled": [false, true],
|
||||
|
||||
@@ -345,21 +345,6 @@ static void setup(Engine* engine, View* view, Scene* scene) {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) {
|
||||
LinearImage result(w, h, 3);
|
||||
filament::math::float3* d = reinterpret_cast<filament::math::float3*>(result.getPixelRef(0, 0));
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
T const* p = reinterpret_cast<T const*>(src + y * bpr);
|
||||
for (size_t x = 0; x < w; ++x, p += 3) {
|
||||
filament::math::float3 sRGB(p[0], p[1], p[2]);
|
||||
sRGB /= std::numeric_limits<T>::max();
|
||||
*d++ = sRGBToLinear(sRGB);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void render(Engine*, View*, Scene*, Renderer*) {
|
||||
int frame = g_currentFrame - FRAME_TO_SKIP - 1;
|
||||
if (frame >= 0 && frame < g_materialVariantCount) {
|
||||
|
||||
@@ -188,17 +188,24 @@ static bool reflectParameters(const MaterialBuilder& builder) {
|
||||
const MaterialBuilder::Parameter& parameter = parameters[i];
|
||||
std::cout << " {" << std::endl;
|
||||
std::cout << R"( "name": ")" << parameter.name.c_str() << "\"," << std::endl;
|
||||
if (parameter.isSampler) {
|
||||
if (parameter.isSampler()) {
|
||||
std::cout << R"( "type": ")" <<
|
||||
Enums::toString(parameter.samplerType) << "\"," << std::endl;
|
||||
std::cout << R"( "format": ")" <<
|
||||
Enums::toString(parameter.samplerFormat) << "\"," << std::endl;
|
||||
Enums::toString(parameter.format) << "\"," << std::endl;
|
||||
std::cout << R"( "precision": ")" <<
|
||||
Enums::toString(parameter.samplerPrecision) << "\"" << std::endl;
|
||||
} else {
|
||||
Enums::toString(parameter.precision) << "\"" << std::endl;
|
||||
} else if (parameter.isUniform()) {
|
||||
std::cout << R"( "type": ")" <<
|
||||
Enums::toString(parameter.uniformType) << "\"," << std::endl;
|
||||
std::cout << R"( "size": ")" << parameter.size << "\"" << std::endl;
|
||||
} else if (parameter.isSubpass()) {
|
||||
std::cout << R"( "type": ")" <<
|
||||
Enums::toString(parameter.subpassType) << "\"," << std::endl;
|
||||
std::cout << R"( "format": ")" <<
|
||||
Enums::toString(parameter.format) << "\"," << std::endl;
|
||||
std::cout << R"( "precision": ")" <<
|
||||
Enums::toString(parameter.precision) << "\"" << std::endl;
|
||||
}
|
||||
std::cout << " }";
|
||||
if (i < count - 1) std::cout << ",";
|
||||
|
||||
@@ -203,6 +203,30 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
|
||||
} else {
|
||||
builder.parameter(type, nameString.c_str());
|
||||
}
|
||||
} else if (Enums::isValid<SubpassType>(typeString)) {
|
||||
if (arraySize > 0) {
|
||||
std::cerr << "parameters: the parameter with name '" << nameString << "'"
|
||||
<< " is an array of subpasses of size " << arraySize << ". Arrays of subpasses"
|
||||
<< " are currently not supported." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
MaterialBuilder::SubpassType type = Enums::toEnum<SubpassType>(typeString);
|
||||
if (precisionValue && formatValue) {
|
||||
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
|
||||
auto precision =
|
||||
Enums::toEnum<SamplerPrecision>(precisionValue->toJsonString()->getString());
|
||||
builder.parameter(type, format, precision, nameString.c_str());
|
||||
} else if (formatValue) {
|
||||
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
|
||||
builder.parameter(type, format, nameString.c_str());
|
||||
} else if (precisionValue) {
|
||||
auto precision =
|
||||
Enums::toEnum<SamplerPrecision>(precisionValue->toJsonString()->getString());
|
||||
builder.parameter(type, precision, nameString.c_str());
|
||||
} else {
|
||||
builder.parameter(type, nameString.c_str());
|
||||
}
|
||||
} else {
|
||||
std::cerr << "parameters: the type '" << typeString
|
||||
<< "' for parameter with name '" << nameString << "' is neither a valid uniform "
|
||||
@@ -219,7 +243,7 @@ static bool processParameters(MaterialBuilder& builder, const JsonishValue& v) {
|
||||
bool ok = true;
|
||||
for (auto value : jsonArray->getElements()) {
|
||||
if (value->getType() == JsonishValue::Type::OBJECT) {
|
||||
ok |= processParameter(builder, *value->toJsonObject());
|
||||
ok &= processParameter(builder, *value->toJsonObject());
|
||||
continue;
|
||||
}
|
||||
std::cerr << "parameters must be an array of OBJECTs." << std::endl;
|
||||
@@ -404,6 +428,14 @@ static bool processOutput(MaterialBuilder& builder, const JsonishObject& jsonObj
|
||||
}
|
||||
}
|
||||
|
||||
const JsonishValue* locationValue = jsonObject.getValue("location");
|
||||
if (locationValue) {
|
||||
if (locationValue->getType() != JsonishValue::NUMBER) {
|
||||
std::cerr << "outputs: location must be a NUMBER." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char* name = nameValue->toJsonString()->getString().c_str();
|
||||
|
||||
OutputTarget target = OutputTarget::COLOR;
|
||||
@@ -425,7 +457,12 @@ static bool processOutput(MaterialBuilder& builder, const JsonishObject& jsonObj
|
||||
qualifier = Enums::toEnum<OutputQualifier>(qualifierValue->toJsonString()->getString());
|
||||
}
|
||||
|
||||
builder.output(qualifier, target, type, name);
|
||||
int location = -1;
|
||||
if (locationValue) {
|
||||
location = static_cast<int>(locationValue->toJsonNumber()->getFloat());
|
||||
}
|
||||
|
||||
builder.output(qualifier, target, type, name, location);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -436,7 +473,7 @@ static bool processOutputs(MaterialBuilder& builder, const JsonishValue& v) {
|
||||
bool ok = true;
|
||||
for (auto value : jsonArray->getElements()) {
|
||||
if (value->getType() == JsonishValue::Type::OBJECT) {
|
||||
ok |= processOutput(builder, *value->toJsonObject());
|
||||
ok &= processOutput(builder, *value->toJsonObject());
|
||||
continue;
|
||||
}
|
||||
std::cerr << "outputs must be an array of OBJECTs." << std::endl;
|
||||
|
||||
16
web/filament-js/filament.d.ts
vendored
16
web/filament-js/filament.d.ts
vendored
@@ -154,7 +154,7 @@ export class Texture$Builder {
|
||||
public levels(levels: number): Texture$Builder;
|
||||
public sampler(sampler: Texture$Sampler): Texture$Builder;
|
||||
public format(format: Texture$InternalFormat): Texture$Builder;
|
||||
public usage(usage: Texture$Usage): Texture$Builder;
|
||||
public usage(usage: number): Texture$Builder;
|
||||
public build(engine: Engine) : Texture;
|
||||
}
|
||||
|
||||
@@ -895,13 +895,13 @@ export enum Texture$Sampler {
|
||||
// It is a "const enum" which means TypeScript will simply create a constant for each member.
|
||||
// It does not contain the $ delimiter to avoid interference with the embind class.
|
||||
export const enum TextureUsage {
|
||||
DEFAULT,
|
||||
COLOR_ATTACHMENT,
|
||||
DEPTH_ATTACHMENT,
|
||||
STENCIL_ATTACHMENT,
|
||||
UPLOADABLE,
|
||||
SAMPLEABLE,
|
||||
SUBPASS_INPUT,
|
||||
COLOR_ATTACHMENT = 1,
|
||||
DEPTH_ATTACHMENT = 2,
|
||||
STENCIL_ATTACHMENT = 4,
|
||||
UPLOADABLE = 8,
|
||||
SAMPLEABLE = 16,
|
||||
SUBPASS_INPUT = 32,
|
||||
DEFAULT = UPLOADABLE | SAMPLEABLE,
|
||||
}
|
||||
|
||||
export enum Texture$CubemapFace {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.9.6",
|
||||
"version": "1.9.7",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user