Compare commits

..

1 Commits

Author SHA1 Message Date
Doris Wu
bd073119d2 Revert "buffer update opt: Add a flag to guard the feature (#9322)"
This reverts commit 49c4a5d62c.
2025-10-15 23:41:03 +08:00
37 changed files with 2325 additions and 13752 deletions

View File

@@ -1,53 +1,43 @@
# This action retrieves the latest commit message from a push or pull_request event
# and makes it available as an output variable named 'msg'.
name: 'Get commit message'
outputs:
msg:
value: ${{ steps.action_output.outputs.msg }}
hash:
value: ${{ steps.action_output.outputs.hash }}
runs:
using: "composite"
steps:
- name: Find commit message (on push)
if: github.event_name == 'push'
shell: bash
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
AUTHOR_NAME="${{ github.event.head_commit.author.name }}"
AUTHOR_EMAIL="${{ github.event.head_commit.author.email }}"
TSTAMP="${{ github.event.head_commit.timestamp }}"
HASH="${{ github.event.head_commit.id }}"
echo "commit $HASH" >> /tmp/commit_msg.txt
echo "commit ${{ github.event.head_commit.id }}" >> /tmp/commit_msg.txt
echo "Author: ${AUTHOR_NAME}<${AUTHOR_EMAIL}>" >> /tmp/commit_msg.txt
echo "Date: ${TSTAMP}" >> /tmp/commit_msg.txt
echo "" >> /tmp/commit_msg.txt
echo "$COMMIT_MESSAGE" >> /tmp/commit_msg.txt
echo "$HASH" > /tmp/commit_hash.txt
echo "${{ github.event.head_commit.message }}" >> /tmp/commit_msg.txt
- name: Find commit message (PR)
shell: bash
id: checkout_code
if: github.event_name == 'pull_request'
run: |
BEFORE_HASH=$(git rev-parse HEAD)
echo "hash=$BEFORE_HASH" >> "$GITHUB_OUTPUT"
# Next we will checkout the actual head (not the merge commits) of the PR
AFTER_HASH="${{ github.event.pull_request.head.sha }}"
git checkout $AFTER_HASH
COMMIT_MESSAGE=$(git log -1 --no-merges)
echo "$COMMIT_MESSAGE" > /tmp/commit_msg.txt
echo "$AFTER_HASH" > /tmp/commit_hash.txt
echo "+++++ head commit message +++++"
echo "$(git log -1 --no-merges)"
echo "+++++++++++++++++++++++++++++++"
echo "hash=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
git checkout ${{ github.event.pull_request.head.sha }}
echo "$(git log -1 --no-merges)" >> /tmp/commit_msg.txt
- shell: bash
id: action_output
run: |
# Get the commit message
DELIMITER="EOF_FILE_CONTENT_$(date +%s)" # Using timestamp to make it more unique
echo "msg<<$DELIMITER" >> "$GITHUB_OUTPUT"
cat /tmp/commit_msg.txt >> "$GITHUB_OUTPUT"
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
# Get the commit hash
echo "hash=$(cat /tmp/commit_hash.txt)" >> "$GITHUB_OUTPUT"
echo "----- got commit message ---"
cat /tmp/commit_msg.txt
echo "----------------------------"
- name: Cleanup Find commit message (PR)
shell: bash
if: github.event_name == 'pull_request'

View File

@@ -23,7 +23,7 @@ jobs:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
run: |
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 test/renderdiff/src/commit_msg.py)
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
git config --global user.email "filament.bot@gmail.com"
git config --global user.name "Filament Bot"
@@ -51,7 +51,7 @@ jobs:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
run: |
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
git config --global user.email "filament.bot@gmail.com"
git config --global user.name "Filament Bot"
git config --global credential.helper cache

View File

@@ -108,7 +108,8 @@ jobs:
uses: ./.github/actions/get-commit-msg
- name: Check for manual edits to /docs
run: |
bash docs_src/build/presubmit_check.sh ${{ steps.get_commit_msg.outputs.hash }}
COMMIT_ID=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
bash docs_src/build/presubmit_check.sh ${COMMIT_ID}
test-renderdiff:
name: test-renderdiff
@@ -120,7 +121,7 @@ jobs:
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-gltf-assets
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- uses: ./.github/actions/get-vulkan-sdk
- id: get_commit_msg
uses: ./.github/actions/get-commit-msg
- name: Prerequisites
@@ -131,14 +132,12 @@ jobs:
shell: bash
- name: Render and compare
id: render_compare
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: |
ls ./gltf/Models
TEST_DIR=test/renderdiff
source ${TEST_DIR}/src/preamble.sh
start_
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 ${TEST_DIR}/src/commit_msg.py)
bash ${TEST_DIR}/generate.sh && \
python3 ${TEST_DIR}/src/golden_manager.py \
--branch=${GOLDEN_BRANCH} \

View File

@@ -159,16 +159,16 @@
<div id="content" class="content">
<main>
<h1 id="rendering-difference-test"><a class="header" href="#rendering-difference-test">Rendering Difference Test</a></h1>
<p>This tool is a collections of scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
<p>We created a few scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
<p>This is mainly useful for continuous integration where GPUs are generally not available on cloud
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s
software rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>.
Additionally, we should be able to use GPUs where available (though this is more of a future
work).</p>
<p>The script <code>render.py</code> contains the core logic for taking input parameters (such as the test
description file) and then running <code>gltf_viewer</code> to produce the renderings.</p>
description file) and then running gltf_viewer to produce the renderings.</p>
<p>In the <code>test</code> directory is a list of test descriptions that are specified in json. Please see
<code>sample.json</code> to glean the structure.</p>
<code>sample.json</code> to parse the structure.</p>
<h2 id="setting-up-python"><a class="header" href="#setting-up-python">Setting up python</a></h2>
<p>The <code>renderdiff</code> project uses <code>python</code> extensively. To install the dependencies for producing
renderings, do the following step</p>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -77,11 +77,11 @@ def check_has_source_edits(commit_hash, printing=True):
# Returns true in a given TAG is found in the commit msg
def commit_msg_has_tag(commit_hash, tag, printing=True):
res, ret = execute(f'git log -n1 --pretty=%B {commit_hash}', cwd=ROOT_DIR)
res, ret = execute(f'git log --pretty=%B {commit_hash}', cwd=ROOT_DIR)
for l in ret.split('\n'):
if tag == l.strip():
if printing:
print(f'Found tag={tag} in commit={commit_hash}')
print(f'Found tag={tag} in commit message')
return True
return False

View File

@@ -84,12 +84,128 @@ constexpr uint32_t UNIFORM_BINDING_INDEX{ 1 };
constexpr uint32_t SAMPLER_BINDING_INDEX{ 2 };
constexpr std::string_view VERTEX_SHADER_ENTRY_POINT{ "vertexShaderMain" };
constexpr std::string_view FRAGMENT_SHADER_ENTRY_POINT{ "fragmentShaderMain" };
// note that the placeholders below must start and end with this prefix and suffix and should not
// otherwise be present in the template:
constexpr std::string_view PLACEHOLDER_PREFIX{ "{{" };
constexpr std::string_view PLACEHOLDER_SUFFIX{ "}}" };
// texture_2d<f32> or texture_multisampled_2d<f32> or texture_3d<f32> or
// texture_depth_2d or texture_depth_multisampled_2d
constexpr std::string_view TEXTURE_TYPE_PLACEHOLDER{ "TEXTURE_TYPE" };
// texture_2d<f32> or texture_depth_2d
[[maybe_unused]] constexpr std::string_view TEXTURE_2D_TYPE_PLACEHOLDER { "TEXTURE_2D_TYPE" };
// "" for no sampler, otherwise "@group(0) @binding(2) var sourceSampler: sampler;"
constexpr std::string_view SAMPLER_DECLARATION_PLACEHOLDER{ "SAMPLER_DECLARATION" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_PLACEHOLDER{ "FRAGMENT_SHADER_SNIPPET" };
// @location(0) vec4<f32> or @builtin(frag_depth) f32
constexpr std::string_view FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE_PLACEHOLDER{
"FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE"
};
constexpr std::string_view SHADER_SOURCE_TEMPLATE{ R"(
struct BlitFragmentShaderArgs {
depthPlane: u32,
scale: vec2<f32>,
sourceOffset: vec2<u32>,
destinationOffset: vec2<u32>,
};
@group(0) @binding(0) var sourceTexture: {{TEXTURE_TYPE}};
@group(0) @binding(1) var<uniform> fragmentShaderArgs: BlitFragmentShaderArgs;
{{SAMPLER_DECLARATION}}
fn getUnnormalizedSourceTextureCoordinates(position: vec2<f32>) -> vec2<f32> {
// These coordinates match the Vulkan vkCmdBlitImage spec:
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
let uvOffset: vec2<f32> = position - vec2<f32>(f32(fragmentShaderArgs.destinationOffset.x),f32(fragmentShaderArgs.destinationOffset.y));
let uvScaled: vec2<f32> = uvOffset * fragmentShaderArgs.scale;
return uvScaled + vec2<f32>(f32(fragmentShaderArgs.sourceOffset.x), f32(fragmentShaderArgs.sourceOffset.y));
}
fn normalize2dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates: vec2<f32>,
sourceDimensions: vec2<u32>) -> vec2<f32> {
return unnormalizedSourceTextureCoordinates /
vec2<f32>(f32(sourceDimensions.x), f32(sourceDimensions.y));
}
fn getNormalized2dSourceTextureCoordinates(
position: vec2<f32>,
sourceTexture: {{TEXTURE_2D_TYPE}}) -> vec2<f32> {
let sourceDimensions: vec2<u32> = textureDimensions(sourceTexture);
return normalize2dSourceTextureCoordinates(
getUnnormalizedSourceTextureCoordinates(position),
sourceDimensions
);
}
fn normalize3dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates: vec2<f32>,
sourceDimensions: vec3<u32>) -> vec3<f32> {
let uvNormalized: vec2<f32> = normalize2dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates,
sourceDimensions.xy
);
return vec3<f32>(
uvNormalized,
(f32(fragmentShaderArgs.depthPlane) + 0.5) / f32(sourceDimensions.z)
);
}
fn getNormalized3dSourceTextureCoordinates(
position: vec2<f32>,
sourceTexture: texture_3d<f32>) -> vec3<f32> {
let sourceDimensions: vec3<u32> = textureDimensions(sourceTexture);
return normalize3dSourceTextureCoordinates(
getUnnormalizedSourceTextureCoordinates(position),
sourceDimensions
);
}
@vertex
fn vertexShaderMain(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {
let fullScreenTriangleVertices = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>( 3.0, -1.0),
vec2<f32>(-1.0, 3.0)
);
return vec4<f32>(fullScreenTriangleVertices[vertexIndex].xy, 0.0, 1.0);
}
{{FRAGMENT_SHADER_SNIPPET}}
)" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_MSAA_INPUT_TEMPLATE{ R"(
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE}} {
var color: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let numberOfSamples: u32 = textureNumSamples(sourceTexture);
let coordinatesF: vec2<f32> = getUnnormalizedSourceTextureCoordinates(position.xy);
let coordinates: vec2<u32> = vec2<u32>(u32(coordinatesF.x), u32(coordinatesF.y));
for (var sampleIndex: u32 = 0; sampleIndex < numberOfSamples; sampleIndex++) {
color += textureLoad(sourceTexture, coordinates, sampleIndex);
}
color /= f32(numberOfSamples);
return color;
}
)" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_3D_INPUT_TEMPLATE{ R"(
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE}} {
let coordinates: vec3<f32> = getNormalized3dSourceTextureCoordinates(position.xy, sourceTexture);
return textureSample(sourceTexture, sourceSampler, coordinates);
}
)" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE{ R"(
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE}} {
let coordinates: vec2<f32> = getNormalized2dSourceTextureCoordinates(position.xy, sourceTexture);
return textureSample(sourceTexture, sourceSampler, coordinates);
}
)" };
} // namespace
// Include the wgsl template sources
#include "WebGPUBlitter_wgsl.inc"
// It can perform a direct memory copy if the formats are compatible and no scaling or format
// conversion is needed. Otherwise, it uses a render pass with a custom shader to perform
// the blit. This allows for scaling, format conversion, and resolving multisampled textures.
@@ -279,25 +395,11 @@ void WebGPUBlitter::blit(wgpu::Queue const& queue, wgpu::CommandEncoder const& c
const bool multisampledSource{ args.source.texture.GetSampleCount() > 1 };
const bool depthSource{ hasDepth(args.source.texture.GetFormat()) };
const bool depthDestination{ hasDepth(args.destination.texture.GetFormat()) };
wgpu::TextureSampleType srcSampleType = {};
if (depthSource) {
srcSampleType = wgpu::TextureSampleType::Depth;
} else if (multisampledSource) {
srcSampleType = wgpu::TextureSampleType::UnfilterableFloat;
} else if (isIntFormat(args.source.texture.GetFormat())) {
srcSampleType = wgpu::TextureSampleType::Sint;
} else if (isUIntFormat(args.source.texture.GetFormat())) {
srcSampleType = wgpu::TextureSampleType::Uint;
} else {
srcSampleType = wgpu::TextureSampleType::Float;
}
const PipelineLayoutKey pipelineLayoutKey{
.sourceDimension = sourceDimension,
.sourceSampleType = srcSampleType,
.filterType = args.filter,
.multisampledSource = multisampledSource,
.depthSource = depthSource,
};
const wgpu::BindGroupDescriptor textureBindGroupDescriptor{
.label = "blit_texture_bind_group",
@@ -352,11 +454,10 @@ void WebGPUBlitter::blit(wgpu::Queue const& queue, wgpu::CommandEncoder const& c
<< "Failed to create render pass encoder for blit.";
const RenderPipelineKey renderPipelineKey{
.sourceDimension = sourceDimension,
.sourceTextureFormat = args.source.texture.GetFormat(),
.sourceSampleType = srcSampleType,
.destinationTextureFormat = args.destination.texture.GetFormat(),
.sourceSampleCount = static_cast<uint8_t>(args.source.texture.GetSampleCount()),
.filterType = args.filter,
.depthSource = depthSource,
};
renderPassEncoder.SetPipeline(getOrCreateRenderPipeline(renderPipelineKey));
renderPassEncoder.SetBindGroup(TEXTURE_BIND_GROUP_INDEX, textureBindGroup);
@@ -456,9 +557,9 @@ wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(RenderPipelineKey const
};
const ShaderModuleKey shaderModuleKey{
.sourceDimension = key.sourceDimension,
.sourceTextureFormat = key.sourceTextureFormat,
.destinationTextureFormat = key.destinationTextureFormat,
.multisampledSource = key.sourceSampleCount > 1,
.depthSource = key.depthSource,
.depthDestination = hasDepth(key.destinationTextureFormat),
};
wgpu::ShaderModule const& shaderModule{ getOrCreateShaderModule(shaderModuleKey) };
const wgpu::FragmentState fragmentState{
@@ -471,9 +572,9 @@ wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(RenderPipelineKey const
};
const PipelineLayoutKey pipelineLayoutKey{
.sourceDimension = key.sourceDimension,
.sourceSampleType = key.sourceSampleType,
.filterType = key.filterType,
.multisampledSource = key.sourceSampleCount > 1,
.depthSource = key.depthSource,
};
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
.label = "render_pass_blit_pipeline",
@@ -562,7 +663,13 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(PipelineLayout
.binding = TEXTURE_BINDING_INDEX,
.visibility = wgpu::ShaderStage::Fragment,
.texture = {
.sampleType = key.sourceSampleType,
.sampleType = key.depthSource
? wgpu::TextureSampleType::Depth
: (key.multisampledSource
? wgpu::TextureSampleType::UnfilterableFloat
: wgpu::TextureSampleType::Float), // only F32 scalar sample
// type supported for now
// (aside from depth)
.viewDimension = key.sourceDimension,
.multisampled = key.multisampledSource,
},
@@ -588,7 +695,7 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(PipelineLayout
};
const wgpu::BindGroupLayoutDescriptor textureBindGroupLayoutDescriptor{
.label = "render_pass_blit_texture_bind_group_layout",
// No need for the last entry (sampler) if source is multisampled.
// TODO, doesnt make any sense but gets rid of the error. Are the entries 0 based?
.entryCount = key.multisampledSource ? (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE - 1)
: (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE),
.entries = bindGroupLayoutEntries,
@@ -613,109 +720,63 @@ wgpu::ShaderModule const& WebGPUBlitter::getOrCreateShaderModule(ShaderModuleKey
// The shader source is generated from a template, with placeholders filled in based on the
// blit configuration (e.g., texture type, sample count).
wgpu::ShaderModule WebGPUBlitter::createShaderModule(ShaderModuleKey const& key) {
bool const isDepthSrc = hasDepth(key.sourceTextureFormat);
bool const isDepthDst = hasDepth(key.destinationTextureFormat);
std::string_view vecDim;
if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
vecDim = "vec3";
std::string_view textureType;
if (key.depthSource) {
if (key.multisampledSource) {
textureType = "texture_depth_multisampled_2d";
} else {
textureType = "texture_depth_2d";
}
} else {
vecDim = "vec2";
if (key.multisampledSource) {
textureType = "texture_multisampled_2d<f32>";
} else {
if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
textureType = "texture_3d<f32>";
} else {
textureType = "texture_2d<f32>";
}
}
}
std::string_view srcPrimType;
std::string_view sampleImpl;
if (isIntFormat(key.sourceTextureFormat)) {
srcPrimType = "<i32>";
sampleImpl = INT_TEXTURE_SAMPLE_IMPL_TEMPLATE;
} else if (isUIntFormat(key.sourceTextureFormat)) {
srcPrimType = "<u32>";
sampleImpl = INT_TEXTURE_SAMPLE_IMPL_TEMPLATE;
} else {
srcPrimType = "<f32>";
sampleImpl = FLOAT_TEXTURE_SAMPLE_IMPL_TEMPLATE;
}
std::string_view fragmentTemplate;
std::string_view srcTextureType;
if (isDepthSrc && key.multisampledSource) {
srcTextureType = "texture_depth_multisampled_2d";
fragmentTemplate = FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE;
} else if (isDepthSrc && !key.multisampledSource) {
srcTextureType = "texture_depth_2d";
fragmentTemplate = FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE;
} else if (key.multisampledSource) {
srcTextureType = "texture_multisampled_2d{{SRC_PRIM_TYPE}}";
fragmentTemplate = FRAGMENT_SHADER_SNIPPET_MSAA_INPUT_TEMPLATE;
} else if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
srcTextureType = "texture_3d{{SRC_PRIM_TYPE}}";
fragmentTemplate = FRAGMENT_SHADER_SNIPPET_3D_INPUT_TEMPLATE;
} else {
srcTextureType = "texture_2d{{SRC_PRIM_TYPE}}";
fragmentTemplate = FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE;
}
std::string_view dstPrimType;
if (isIntFormat(key.destinationTextureFormat)) {
dstPrimType = "<i32>";
} else if (isUIntFormat(key.destinationTextureFormat)) {
dstPrimType = "<u32>";
} else {
dstPrimType = "<f32>";
}
std::string_view retAttribute;
std::string_view retType;
if (isDepthDst) {
retAttribute = "@builtin(frag_depth)";
retType = "f32";
} else {
retAttribute = "@location(0)";
retType = "vec4{{DST_PRIM_TYPE}}";
}
using ReplacementMap = std::unordered_map<std::string_view, std::string_view>;
auto const replace = [&](std::string_view src, ReplacementMap const& replacements) {
return webgpuutils::processPlaceholderTemplate(
src, PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, replacements);
const std::string_view texture2dType{ key.depthSource ? "texture_depth_2d"
: "texture_2d<f32>" };
// we don't declare a sampler in the shader or pipeline for the multisampled case
const std::string_view samplerDeclaration{
key.multisampledSource ? "" : "@group(0) @binding(2) var sourceSampler: sampler;"
};
// The dependency chain is shader module -> fragment source -> sampling function
// Hence we need to fill in the templates in reverse order.
// Fill in sampling implementation in the sampling func
std::string source = replace(TEXTURE_SAMPLE_IMPL_MAIN_TEMPLATE, {
{ TEXTURE_SAMPLE_IMPL, sampleImpl },
});
// Fill in sampling func in the fragment
source = replace(fragmentTemplate, {
{ TEXTURE_SAMPLE_IMPL_MAIN, source },
});
// Fill in fragment in the shader module, first pass
source = replace(SHADER_SOURCE_TEMPLATE, {
{ FRAGMENT_SHADER_SNIPPET, source },
});
// Fill in fragment in the shader module, second pass
source = replace(source, {
{ TEXTURE_TYPE, srcTextureType },
{ RET_ATTRIBUTE, retAttribute },
{ RET_TYPE, retType },
});
// Fill in fragment in the shader module, third pass
// TEXTURE_TYPE, RET_ATTRIBUTE, RET_TYPE have a dependency on SRC_PRIM_TYPE, DST_PRIM_TYPE,
// VECTOR_DIM so we need another pass to fill them in.
source = replace(source, {
{ VECTOR_DIM, vecDim },
{ SRC_PRIM_TYPE, srcPrimType },
{ DST_PRIM_TYPE, dstPrimType },
});
const std::string_view fragmentReturnAttributeAndType{
key.depthDestination ? "@builtin(frag_depth) f32" : "@location(0) vec4<f32>"
};
const std::unordered_map<std::string_view, std::string_view>
valueByPlaceholderNameForFragmentSnippet{
{ FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE_PLACEHOLDER, fragmentReturnAttributeAndType },
};
std::string fragmentShaderSnippet;
if (key.multisampledSource) {
fragmentShaderSnippet = webgpuutils::processPlaceholderTemplate(
FRAGMENT_SHADER_SNIPPET_MSAA_INPUT_TEMPLATE, PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX,
valueByPlaceholderNameForFragmentSnippet);
} else {
if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
fragmentShaderSnippet = webgpuutils::processPlaceholderTemplate(
FRAGMENT_SHADER_SNIPPET_3D_INPUT_TEMPLATE, PLACEHOLDER_PREFIX,
PLACEHOLDER_SUFFIX, valueByPlaceholderNameForFragmentSnippet);
} else {
fragmentShaderSnippet = webgpuutils::processPlaceholderTemplate(
FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE, PLACEHOLDER_PREFIX,
PLACEHOLDER_SUFFIX, valueByPlaceholderNameForFragmentSnippet);
}
}
const std::unordered_map<std::string_view, std::string_view> valueByPlaceholderNameForModule{
{ TEXTURE_TYPE_PLACEHOLDER, textureType },
{ TEXTURE_2D_TYPE_PLACEHOLDER, texture2dType },
{ SAMPLER_DECLARATION_PLACEHOLDER, samplerDeclaration },
{ FRAGMENT_SHADER_SNIPPET_PLACEHOLDER, fragmentShaderSnippet },
};
const std::string shaderSource{ webgpuutils::processPlaceholderTemplate(SHADER_SOURCE_TEMPLATE,
PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, valueByPlaceholderNameForModule) };
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.code = source.data();
wgslDescriptor.code = shaderSource.data();
const wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
.nextInChain = &wgslDescriptor,
.label = "render_pass_blit_shaders",

View File

@@ -97,12 +97,11 @@ private:
struct RenderPipelineKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
wgpu::TextureFormat sourceTextureFormat; // 4
wgpu::TextureSampleType sourceSampleType; // 4
wgpu::TextureFormat destinationTextureFormat; // 4
uint8_t sourceSampleCount; // 1
SamplerMagFilter filterType; // 1
uint8_t padding[2] = {}; // 2
bool depthSource; // 1
uint8_t padding = 0; // 1
bool operator==(const RenderPipelineKey& other) const;
using Hasher = utils::hash::MurmurHashFn<RenderPipelineKey>;
@@ -110,10 +109,10 @@ private:
struct PipelineLayoutKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
wgpu::TextureSampleType sourceSampleType; // 4
SamplerMagFilter filterType; // 1
bool multisampledSource; // 1
uint8_t padding[2] = {}; // 2
bool depthSource; // 1
uint8_t padding = 0; // 1
bool operator==(const PipelineLayoutKey& other) const;
using Hasher = utils::hash::MurmurHashFn<PipelineLayoutKey>;
@@ -121,20 +120,20 @@ private:
struct ShaderModuleKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
wgpu::TextureFormat sourceTextureFormat; // 4
wgpu::TextureFormat destinationTextureFormat; // 4
bool multisampledSource; // 1
uint8_t padding0[3] = {}; // 3
bool depthSource; // 1
bool depthDestination; // 1
uint8_t padding = 0; // 1
bool operator==(const ShaderModuleKey& other) const;
using Hasher = utils::hash::MurmurHashFn<ShaderModuleKey>;
};
static_assert(sizeof(RenderPipelineKey) == 20,
static_assert(sizeof(RenderPipelineKey) == 12,
"RenderPipelineKey must not have implicit padding.");
static_assert(sizeof(PipelineLayoutKey) == 12,
static_assert(sizeof(PipelineLayoutKey) == 8,
"PipelineLayoutKey must not have implicit padding.");
static_assert(sizeof(ShaderModuleKey) == 16, "ShaderModuleKey must not have implicit padding.");
static_assert(sizeof(ShaderModuleKey) == 8, "ShaderModuleKey must not have implicit padding.");
wgpu::Device mDevice;
wgpu::Sampler mNearestSampler{ nullptr };

View File

@@ -1,174 +0,0 @@
/*
* Copyright (C) 2025 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.
*/
/*
* NOTE: This file is meant to be included with #include in WebGPUBlitter.cpp
* It is separated out for clarity.
*/
namespace {
// note that the placeholders below must start and end with this prefix and suffix and should not
// otherwise be present in the template:
constexpr std::string_view PLACEHOLDER_PREFIX{ "{{" };
constexpr std::string_view PLACEHOLDER_SUFFIX{ "}}" };
#define PLACEHOLDER_DEF(name) constexpr std::string_view name = #name
PLACEHOLDER_DEF(FRAGMENT_SHADER_SNIPPET);
PLACEHOLDER_DEF(TEXTURE_SAMPLE_IMPL);
PLACEHOLDER_DEF(TEXTURE_SAMPLE_IMPL_MAIN);
// texture_multisampled_2d<f32>
// texture_2d<f32/i32/u32>
// texture_3d<f32/i32/u32>
// texture_depth_2d
// texture_depth_multisampled_2d
PLACEHOLDER_DEF(TEXTURE_TYPE);
// vec2, vec3
PLACEHOLDER_DEF(VECTOR_DIM);
// vec_<f32>, vec_<u32>, vec_<i32>, f32
PLACEHOLDER_DEF(RET_TYPE);
// @builtin(frag_depth), @location(0)
PLACEHOLDER_DEF(RET_ATTRIBUTE);
// <f32>, <u32>, <i32>
PLACEHOLDER_DEF(DST_PRIM_TYPE);
PLACEHOLDER_DEF(SRC_PRIM_TYPE);
#undef PLACEHOLDER_DEF
constexpr std::string_view SHADER_SOURCE_TEMPLATE{ R"(
struct BlitFragmentShaderArgs {
depthPlane: u32,
scale: vec2<f32>,
sourceOffset: vec2<u32>,
destinationOffset: vec2<u32>,
};
@group(0) @binding(0) var sourceTexture: {{TEXTURE_TYPE}};
@group(0) @binding(1) var<uniform> fragmentShaderArgs: BlitFragmentShaderArgs;
fn getUnnormalizedSourceTextureCoordinates(position: vec2<f32>) -> vec2<f32> {
// These coordinates match the Vulkan vkCmdBlitImage spec:
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
let uvOffset: vec2<f32> = position - vec2<f32>(f32(fragmentShaderArgs.destinationOffset.x),f32(fragmentShaderArgs.destinationOffset.y));
let uvScaled: vec2<f32> = uvOffset * fragmentShaderArgs.scale;
return uvScaled + vec2<f32>(f32(fragmentShaderArgs.sourceOffset.x), f32(fragmentShaderArgs.sourceOffset.y));
}
fn normalize2dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates: vec2<f32>,
sourceDimensions: vec2<u32>) -> vec2<f32> {
return unnormalizedSourceTextureCoordinates /
vec2<f32>(f32(sourceDimensions.x), f32(sourceDimensions.y));
}
@vertex
fn vertexShaderMain(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {
let fullScreenTriangleVertices = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>( 3.0, -1.0),
vec2<f32>(-1.0, 3.0)
);
return vec4<f32>(fullScreenTriangleVertices[vertexIndex].xy, 0.0, 1.0);
}
{{FRAGMENT_SHADER_SNIPPET}}
)"
};
constexpr std::string_view FLOAT_TEXTURE_SAMPLE_IMPL_TEMPLATE{ R"(
return textureSample(sourceTexture, sourceSampler, coordinates);
)" };
constexpr std::string_view INT_TEXTURE_SAMPLE_IMPL_TEMPLATE { R"(
let texelCoords = {{VECTOR_DIM}}<u32>(coordinates * {{VECTOR_DIM}}<f32>(sourceDimensions));
return textureLoad(sourceTexture, texelCoords, 0);
)"};
constexpr std::string_view TEXTURE_SAMPLE_IMPL_MAIN_TEMPLATE { R"(
// Assumes that "@group(0) @binding(2) var sourceSampler: sampler;" has been declared in the main program;
fn sampleTextureImpl(sourceTexture: {{TEXTURE_TYPE}}, sourceDimensions: {{VECTOR_DIM}}<u32>,
coordinates: {{VECTOR_DIM}}<f32>) -> vec4{{DST_PRIM_TYPE}} {
{{TEXTURE_SAMPLE_IMPL}}
}
)"};
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_MSAA_INPUT_TEMPLATE{ R"(
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{RET_ATTRIBUTE}} {{RET_TYPE}} {
var color: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);
let numberOfSamples: u32 = textureNumSamples(sourceTexture);
let coordinatesF: vec2<f32> = getUnnormalizedSourceTextureCoordinates(position.xy);
let coordinates: vec2<u32> = vec2<u32>(u32(coordinatesF.x), u32(coordinatesF.y));
for (var sampleIndex: u32 = 0; sampleIndex < numberOfSamples; sampleIndex++) {
color += textureLoad(sourceTexture, coordinates, sampleIndex);
}
color /= f32(numberOfSamples);
return color;
}
)" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_3D_INPUT_TEMPLATE{ R"(
@group(0) @binding(2) var sourceSampler: sampler;
{{TEXTURE_SAMPLE_IMPL_MAIN}}
fn normalize3dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates: vec2<f32>,
sourceDimensions: vec3<u32>) -> vec3<f32> {
let uvNormalized: vec2<f32> = normalize2dSourceTextureCoordinates(
unnormalizedSourceTextureCoordinates,
sourceDimensions.xy
);
return vec3<f32>(
uvNormalized,
(f32(fragmentShaderArgs.depthPlane) + 0.5) / f32(sourceDimensions.z)
);
}
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{RET_ATTRIBUTE}} {{RET_TYPE}} {
let sourceDimensions: vec3<u32> = textureDimensions(sourceTexture);
let coordinates: vec3<f32> = normalize3dSourceTextureCoordinates(
getUnnormalizedSourceTextureCoordinates(position),
sourceDimensions
);
return sampleTextureImpl(sourceTexture, sourceDimensions, coordinates);
}
)" };
constexpr std::string_view FRAGMENT_SHADER_SNIPPET_2D_INPUT_TEMPLATE { R"(
@group(0) @binding(2) var sourceSampler: sampler;
{{TEXTURE_SAMPLE_IMPL_MAIN}}
@fragment
fn fragmentShaderMain(@builtin(position) position: vec4<f32>) -> {{RET_ATTRIBUTE}} {{RET_TYPE}} {
let sourceDimensions: vec2<u32> = textureDimensions(sourceTexture);
let coordinates: vec2<f32> = normalize2dSourceTextureCoordinates(
getUnnormalizedSourceTextureCoordinates(position.xy),
sourceDimensions
);
return sampleTextureImpl(sourceTexture, sourceDimensions, coordinates);
}
)" };
} // namespace

View File

@@ -446,7 +446,6 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes,
utils::ImmutableCString&& tag) {
// Hello world! This is a test for pushing branch to Github
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
attributes, mDeviceLimits);
@@ -1498,7 +1497,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
mReadPixelMapsCounter.startTask();
userData->buffer.MapAsync(
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {

View File

@@ -41,45 +41,6 @@ namespace filament::backend {
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8;
}
[[nodiscard]] constexpr bool isUIntFormat(const wgpu::TextureFormat format) {
// see https://www.w3.org/TR/webgpu/#texture-formats
// and https://www.w3.org/TR/webgpu/#texture-format-caps
switch (format) {
case wgpu::TextureFormat::R8Uint:
case wgpu::TextureFormat::R16Uint:
case wgpu::TextureFormat::RG8Uint:
case wgpu::TextureFormat::R32Uint:
case wgpu::TextureFormat::RG16Uint:
case wgpu::TextureFormat::RGBA8Uint:
case wgpu::TextureFormat::RGB10A2Uint:
case wgpu::TextureFormat::RG32Uint:
case wgpu::TextureFormat::RGBA16Uint:
case wgpu::TextureFormat::RGBA32Uint:
return true;
default:
return false;
}
}
[[nodiscard]] constexpr bool isIntFormat(const wgpu::TextureFormat format) {
// see https://www.w3.org/TR/webgpu/#texture-formats
// and https://www.w3.org/TR/webgpu/#texture-format-caps
switch (format) {
case wgpu::TextureFormat::R8Sint:
case wgpu::TextureFormat::R16Sint:
case wgpu::TextureFormat::RG8Sint:
case wgpu::TextureFormat::R32Sint:
case wgpu::TextureFormat::RG16Sint:
case wgpu::TextureFormat::RGBA8Sint:
case wgpu::TextureFormat::RG32Sint:
case wgpu::TextureFormat::RGBA16Sint:
case wgpu::TextureFormat::RGBA32Sint:
return true;
default:
return false;
}
}
[[nodiscard]] constexpr std::string_view toString(const PixelDataFormat format) {
switch (format) {
case PixelDataFormat::R: return "R";

View File

@@ -52,14 +52,15 @@ std::string processPlaceholderTemplate(std::string_view const& stringTemplate,
"Malformed source with missing suffix to placeholder");
const std::string_view placeholderName{ std::string_view(sourceData + positionOfPlaceholder,
positionAfterPlaceholder - positionOfPlaceholder) };
if (const auto iter{ valueByPlaceholderName.find(placeholderName) };
iter != valueByPlaceholderName.end()) {
iter == valueByPlaceholderName.end()) {
// wrapping placeholderName in a std::string to null terminate the C string....
PANIC_POSTCONDITION("Found placeholder '%s' in template, but this is not present in "
"valueByPlaceholderName",
std::string{ placeholderName }.data());
} else {
const std::string_view value{ iter->second };
out << value;
} else {
// It's ok to not replace a template item. We assume replacement can take multiple passes.
out << placeholderPrefix << placeholderName << placeholderSuffix;
}
// update the cursor for after the placeholder...
positionCursorInTemplateString = positionAfterPlaceholder + placeholderSuffix.size();

View File

@@ -23,17 +23,13 @@
namespace test {
TestArguments parseArguments(int argc, char* argv[]) {
TestArguments arguments = {};
arguments.backend = Backend::OPENGL;
Backend parseArgumentsForBackend(int argc, char* argv[]) {
Backend backend = Backend::OPENGL;
// The first colon in OPTSTR turns on silent error reporting. This is important, as the
// arguments may also contain gtest parameters we don't know about.
static constexpr const char* OPTSTR = ":a:kc";
static constexpr const char* OPTSTR = ":a:";
static const struct option OPTIONS[] = {
{ "api", required_argument, nullptr, 'a' },
{ "headless_only", no_argument, nullptr, 'k' },
{ "ci", no_argument, nullptr, 'c' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
@@ -45,13 +41,13 @@ TestArguments parseArguments(int argc, char* argv[]) {
switch (opt) {
case 'a':
if (arg == "opengl") {
arguments.backend = Backend::OPENGL;
backend = Backend::OPENGL;
} else if (arg == "vulkan") {
arguments.backend = Backend::VULKAN;
backend = Backend::VULKAN;
} else if (arg == "metal") {
arguments.backend = Backend::METAL;
backend = Backend::METAL;
} else if (arg == "webgpu") {
arguments.backend = Backend::WEBGPU;
backend = Backend::WEBGPU;
} else {
std::cerr << "Unrecognized target API. Must be 'opengl'|'vulkan'|'metal'|'webgpu'."
<< std::endl
@@ -59,16 +55,10 @@ TestArguments parseArguments(int argc, char* argv[]) {
<< std::endl;
}
break;
case 'k':
arguments.headlessOnly = true;
break;
case 'c':
arguments.isContinuousIntegration = true;
break;
}
}
return arguments;
return backend;
}
} // namespace test

View File

@@ -41,7 +41,6 @@ enum class OperatingSystem: uint8_t {
LINUX = 2,
// Also represents iOS phones.
APPLE = 3,
CONTINUOUS_INTEGRATION = 4,
// TODO: When tests support windows add it here.
};
@@ -74,17 +73,11 @@ void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile,
*/
int runTests();
struct TestArguments {
Backend backend;
bool headlessOnly = false;
bool isContinuousIntegration = false;
};
/**
* A utility method that can be invoked by test runners to parse arguments.
* Looks through the provided command-line arguments and finds any -a <backend> arguments.
*/
TestArguments parseArguments(int argc, char* argv[]);
Backend parseArgumentsForBackend(int argc, char* argv[]);
} // namespace test

View File

@@ -43,10 +43,7 @@ std::array<test::Backend, 2> const VALID_BACKENDS{
}// namespace
int main(int argc, char* argv[]) {
const auto arguments = test::parseArguments(argc, argv);
const auto backend = arguments.backend;
// Note that Linux is headless-only.
auto backend = test::parseArgumentsForBackend(argc, argv);
if (!std::any_of(VALID_BACKENDS.begin(), VALID_BACKENDS.end(),
[backend](test::Backend validBackend) { return backend == validBackend; })) {
@@ -54,8 +51,6 @@ int main(int argc, char* argv[]) {
return 1;
}
const auto operatingSystem = arguments.isContinuousIntegration ?
test::OperatingSystem::CONTINUOUS_INTEGRATION : test::OperatingSystem::LINUX;
test::initTests(backend, operatingSystem, false, argc, argv);
test::initTests(backend, test::OperatingSystem::LINUX, false, argc, argv);
return test::runTests();
}

View File

@@ -32,34 +32,29 @@ test::NativeView getNativeView() {
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property test::Backend backend;
@property bool headlessOnly;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if (self.headlessOnly) {
nativeView.ptr = nullptr;
nativeView.width = test::WINDOW_WIDTH;
nativeView.height = test::WINDOW_HEIGHT;
} else {
NSView* view = [self createView];
switch (self.backend) {
case test::Backend::OPENGL:
nativeView.ptr = (void*) view;
break;
case test::Backend::METAL:
case test::Backend::VULKAN:
case test::Backend::WEBGPU:
case test::Backend::NOOP:
nativeView.ptr = (void*) view.layer;
break;
}
CGSize drawableSize = ((CAMetalLayer*) view.layer).drawableSize;
nativeView.width = static_cast<size_t>(drawableSize.width);
nativeView.height = static_cast<size_t>(drawableSize.height);
NSView* view = [self createView];
if (self.backend == test::Backend::OPENGL) {
nativeView.ptr = (void*) view;
}
if (self.backend == test::Backend::METAL) {
nativeView.ptr = (void*) view.layer;
}
if (self.backend == test::Backend::VULKAN) {
nativeView.ptr = (void*) view.layer;
}
if (self.backend == test::Backend::WEBGPU) {
nativeView.ptr = (void*) view.layer;
}
CGSize drawableSize = ((CAMetalLayer*) view.layer).drawableSize;
nativeView.width = static_cast<size_t>(drawableSize.width);
nativeView.height = static_cast<size_t>(drawableSize.height);
exit(test::runTests());
}
@@ -105,25 +100,11 @@ test::NativeView getNativeView() {
@end
int main(int argc, char* argv[]) {
const auto arguments = test::parseArguments(argc, argv);
const auto operatingSystem = arguments.isContinuousIntegration ?
test::OperatingSystem::CONTINUOUS_INTEGRATION : test::OperatingSystem::APPLE;
test::initTests(arguments.backend, operatingSystem, false, argc, argv);
NSApplication* app = [NSApplication sharedApplication];
auto backend = test::parseArgumentsForBackend(argc, argv);
test::initTests(backend, test::OperatingSystem::APPLE, false, argc, argv);
AppDelegate* delegate = [AppDelegate new];
delegate.backend = arguments.backend;
delegate.headlessOnly = arguments.headlessOnly;
delegate.backend = backend;
NSApplication* app = [NSApplication sharedApplication];
[app setDelegate:delegate];
if (arguments.headlessOnly) {
// In headless mode, we don't want to start the NSApplication event loop.
// Instead, we can manually "finish" launching the app, which will trigger the tests to run.
[app finishLaunching];
[delegate applicationDidFinishLaunching:nil];
// The line above calls exit(), so we should not reach here.
return 0;
}
[app run];
}

View File

@@ -158,7 +158,7 @@ TEST_F(BlitTest, ColorMagnify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
@@ -222,7 +222,7 @@ TEST_F(BlitTest, ColorMinify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
@@ -371,7 +371,7 @@ TEST_F(BlitTest, Blit2DTextureArray) {
constexpr int kDstTexLayer = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
@@ -437,7 +437,7 @@ TEST_F(BlitTest, BlitRegion) {
constexpr int kDstLevel = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.

View File

@@ -103,7 +103,7 @@ TEST_F(BackendTest, FrameCompletedCallback) {
Cleanup cleanup(api);
// Create a SwapChain.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,

View File

@@ -22,7 +22,6 @@
#include "SharedShaders.h"
#include "SharedShadersConstants.h"
#include "Skip.h"
#include "Workarounds.h"
#include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h>
@@ -56,7 +55,7 @@ protected:
auto colorTexture = cleanup.add(
api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
screenWidth(), screenHeight(), 1,
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE TEXTURE_USAGE_READ_PIXELS));
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0,
screenWidth(), screenHeight(), 1, 1, { { colorTexture } }, {}, {}));
return renderTarget;
@@ -91,27 +90,19 @@ protected:
return { vbih, vbh, ibh };
}
std::pair<Shader, DescriptorSetHandle> getSimpleShader(Cleanup& cleanup,
math::float4 const& color) {
Shader getSimpleShader(Cleanup& cleanup, math::float4 const& color) {
auto& api = getDriverApi();
Shader const shader = SharedShaders::makeShader(getDriverApi(), cleanup,
ShaderRequest{
.mVertexType = VertexShaderType::Simple,
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::Simple,
});
Shader const shader = SharedShaders::makeShader(getDriverApi(), cleanup, ShaderRequest{
.mVertexType = VertexShaderType::Simple,
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::Simple
});
auto descSet = shader.createDescriptorSet(api);
UniformBindingConfig uboBindingConfig = {
.descriptorSet = descSet,
};
auto const ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC_BIT));
// This will bind the UBO to descSet and also bind descSet. But we also need to manually
// bind descset every begin/end frame.
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, uboBindingConfig);
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{ .color = color });
return { shader, descSet };
return shader;
}
void copyData(const void* data, size_t const size, size_t const offset,
@@ -131,9 +122,7 @@ protected:
int64_t const frame,
SwapChainHandle swapChain,
RenderTargetHandle renderTarget,
RenderPrimitiveHandle renderPrimitive,
DescriptorSetHandle descSet,
PipelineState const& state) {
RenderPrimitiveHandle renderPrimitive, PipelineState const& state) {
auto& api = getDriverApi();
RenderPassParams params = getClearColorRenderPass({0,0,0,1});
params.viewport = getFullViewport();
@@ -141,8 +130,6 @@ protected:
api.beginFrame(frame, 0, 0);
api.beginRenderPass(renderTarget, params);
api.bindPipeline(state);
// The binding index is always 0 for the simple shaders we're using.
api.bindDescriptorSet(descSet, 0, {});
api.bindRenderPrimitive(renderPrimitive);
api.draw2(0, count, 1);
api.endRenderPass();
@@ -198,7 +185,7 @@ protected:
flushAndWait();
EXPECT_EQ(callbackExecuted, 1);
auto [shader, descset] = getSimpleShader(cleanup, color);
Shader const shader = getSimpleShader(cleanup, color);
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 3, stride, baseVertex, bufferObject);
@@ -210,7 +197,7 @@ protected:
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = vbih;
render(3, 0, swapChain, renderTarget, renderPrimitive, descset, state);
render(3, 0, swapChain, renderTarget, renderPrimitive, state);
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), screenshotName, 0));
}
@@ -301,7 +288,7 @@ TEST_F(MemoryMappedTest, MultipleCopies) {
flushAndWait();
EXPECT_EQ(callbacksExecuted, 3);
auto [shader, descset] = getSimpleShader(cleanup, { 1, 1, 0, 1 });
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
@@ -313,7 +300,7 @@ TEST_F(MemoryMappedTest, MultipleCopies) {
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = vbih;
render(9, 0, swapChain, renderTarget, renderPrimitive, descset, state);
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "MultipleCopies", 0));
}
@@ -358,7 +345,7 @@ TEST_F(MemoryMappedTest, UpdatePartial) {
}
auto [shader, descset] = getSimpleShader(cleanup, { 1, 1, 0, 1 });
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
@@ -370,7 +357,7 @@ TEST_F(MemoryMappedTest, UpdatePartial) {
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = vbih;
render(9, 0, swapChain, renderTarget, renderPrimitive, descset, state);
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_before", 0));
@@ -393,7 +380,7 @@ TEST_F(MemoryMappedTest, UpdatePartial) {
api.makeCurrent(swapChain, swapChain);
// Second render, after update
render(9, 1, swapChain, renderTarget, renderPrimitive, descset, state);
render(9, 1, swapChain, renderTarget, renderPrimitive, state);
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_after", 0));
}

View File

@@ -385,7 +385,8 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = cleanup.add(
api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{

View File

@@ -70,7 +70,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup,
@@ -149,7 +149,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, cleanup, ShaderRequest{

View File

@@ -33,7 +33,8 @@ TEST_F(BackendTest, TestTemplate) {
auto& api = getDriverApi();
Cleanup cleanup(api);
auto swapChain = cleanup.add(createSwapChain());
auto swapChain =
cleanup.add(api.createSwapChainHeadless(kRenderTargetSize, kRenderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
RenderTargetHandle renderTarget = cleanup.add(api.createDefaultRenderTarget());

View File

@@ -776,7 +776,6 @@ public:
} backend;
struct {
bool check_crc32_after_loading = false;
bool enable_material_instance_uniform_batching = false;
} material;
} features;
@@ -826,9 +825,6 @@ public:
{ "material.check_crc32_after_loading",
"Verify the checksum of package data when a material is loaded.",
&features.material.check_crc32_after_loading, false },
{ "material.enable_material_instance_uniform_batching",
"Make all MaterialInstances share a common large uniform buffer and use sub-allocations within it.",
&features.material.enable_material_instance_uniform_batching, false },
}};
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept {

View File

@@ -60,13 +60,8 @@ namespace filament {
using namespace backend;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept
: FMaterialInstance(engine, material, name,
engine.features.material.enable_material_instance_uniform_batching) {
}
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name, bool useUboBatching) noexcept : mMaterial(material),
const char* name) noexcept
: mMaterial(material),
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK),
mShadowCulling(CullingMode::BACK),
@@ -76,7 +71,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false),
mIsDoubleSided(false),
mIsDefaultInstance(false),
mUseUboBatching(useUboBatching),
mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) {
@@ -86,16 +80,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
// expected by the per-material descriptor-set layout
size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize());
mUniforms = UniformBuffer(uboSize);
mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::STATIC, utils::ImmutableCString{ material->getName().c_str_safe() });
if (mUseUboBatching) {
mUboData = BufferAllocator::UNALLOCATED;
} else {
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::STATIC, ImmutableCString{ material->getName().c_str_safe() });
// set the UBO, always descriptor 0
mDescriptorSet.setBuffer(material->getDescriptorSetLayout(),
0, std::get<Handle<HwBufferObject>>(mUboData), 0, mUniforms.getSize());
}
// set the UBO, always descriptor 0
mDescriptorSet.setBuffer(material->getDescriptorSetLayout(),
0, mUbHandle, 0, mUniforms.getSize());
const RasterState& rasterState = material->getRasterState();
// At the moment, only MaterialInstances have a stencil state, but in the future it should be
@@ -150,7 +140,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mHasScissor(false),
mIsDoubleSided(other->mIsDoubleSided),
mIsDefaultInstance(false),
mUseUboBatching(other->mUseUboBatching),
mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) {
@@ -158,16 +147,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
FMaterial const* const material = other->getMaterial();
mUniforms.setUniforms(other->getUniformBuffer());
mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() });
if (mUseUboBatching) {
mUboData = BufferAllocator::UNALLOCATED;
} else {
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() });
// set the UBO, always descriptor 0
mDescriptorSet.setBuffer(material->getDescriptorSetLayout(),
0, std::get<Handle<HwBufferObject>>(mUboData), 0, mUniforms.getSize());
}
// set the UBO, always descriptor 0
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(),
0, mUbHandle, 0, mUniforms.getSize());
if (material->hasDoubleSidedCapability()) {
setDoubleSided(mIsDoubleSided);
@@ -188,7 +173,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
material->getId(), material->generateMaterialInstanceId());
// If the original descriptor set has been commited, the copy needs to commit as well.
if (!mUseUboBatching && other->mDescriptorSet.getHandle()) {
if (other->mDescriptorSet.getHandle()) {
mDescriptorSet.commitSlow(mMaterial->getDescriptorSetLayout(), driver);
}
}
@@ -205,16 +190,13 @@ FMaterialInstance::~FMaterialInstance() noexcept = default;
void FMaterialInstance::terminate(FEngine& engine) {
FEngine::DriverApi& driver = engine.getDriverApi();
mDescriptorSet.terminate(driver);
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
if (ubHandle){
driver.destroyBufferObject(*ubHandle);
}
driver.destroyBufferObject(mUbHandle);
}
void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driver) {
mHasStreamUniformAssociations = false;
if (!mTextureParameters.empty()) {
BufferObjectStreamDescriptor descriptor;
backend::BufferObjectStreamDescriptor descriptor;
for (auto const& [binding, p]: mTextureParameters) {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getTransformFieldOffset(binding);
if (offset >= 0) {
@@ -224,12 +206,7 @@ void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driv
}
}
if (descriptor.mStreams.size() > 0) {
// UBO batching is incompatible with stream uniform associations because streams require a
// dedicated UBO handle, not a sub-allocation. Assert that any instance here uses its own UBO.
assert_invariant(!mUseUboBatching);
auto const* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
assert_invariant(ubHandle);
driver.registerBufferObjectStreams(*ubHandle, std::move(descriptor));
driver.registerBufferObjectStreams(mUbHandle, std::move(descriptor));
}
}
}
@@ -240,17 +217,11 @@ void FMaterialInstance::commit(FEngine& engine) const {
}
}
void FMaterialInstance::commit(FEngine::DriverApi& driver) const {
void FMaterialInstance::commit(DriverApi& driver) const {
// update uniforms if needed
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean();
if (mUseUboBatching) {
// TODO: update the content by `copyToMemoryMappedBuffer`
}
else {
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
assert_invariant(ubHandle != nullptr);
driver.updateBufferObject(*ubHandle, mUniforms.toBufferDescriptor(driver), 0);
}
driver.updateBufferObject(mUbHandle, mUniforms.toBufferDescriptor(driver), 0);
}
if (!mTextureParameters.empty()) {
for (auto const& [binding, p]: mTextureParameters) {
@@ -440,21 +411,6 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
}
void FMaterialInstance::assignUboAllocation(
const Handle<HwBufferObject>& ubHandle,
BufferAllocator::AllocationId id,
BufferAllocator::allocation_size_t offset) {
assert_invariant(mUseUboBatching);
mUboData = id;
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, offset,
mUniforms.getSize());
}
BufferAllocator::AllocationId FMaterialInstance::getAllocationId() const noexcept {
auto const* allocationId = std::get_if<BufferAllocator::AllocationId>(&mUboData);
return allocationId ? *allocationId : BufferAllocator::UNALLOCATED;
}
void FMaterialInstance::fixMissingSamplers() const {
// Here we check that all declared sampler parameters are set, this is required by
// Vulkan and Metal; GL is more permissive. If a sampler parameter is not set, we will

View File

@@ -23,7 +23,6 @@
#include "ds/DescriptorSet.h"
#include "details/BufferAllocator.h"
#include "details/Engine.h"
#include "private/backend/DriverApi.h"
@@ -58,9 +57,6 @@ class FMaterialInstance : public MaterialInstance {
public:
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept;
// Use this constructor when you need to override the ubo batching flag for an individual MI.
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name, bool useUboBatching) noexcept;
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
@@ -79,12 +75,6 @@ public:
void use(FEngine::DriverApi& driver, Variant variant = {}) const;
void assignUboAllocation(const backend::Handle<backend::HwBufferObject>& ubHandle,
BufferAllocator::AllocationId id,
BufferAllocator::allocation_size_t offset);
BufferAllocator::AllocationId getAllocationId() const noexcept;
FMaterial const* getMaterial() const noexcept { return mMaterial; }
uint64_t getSortingKey() const noexcept { return mMaterialSortingKey; }
@@ -244,8 +234,6 @@ public:
return mIsDefaultInstance;
}
bool isUsingUboBatching() const noexcept { return mUseUboBatching; }
// Called by the engine to ensure that unset samplers are initialized with placedholders.
void fixMissingSamplers() const;
@@ -286,7 +274,7 @@ private:
backend::SamplerParams params;
};
std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData;
backend::Handle<backend::HwBufferObject> mUbHandle;
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms;
@@ -308,7 +296,6 @@ private:
bool mHasScissor : 1;
bool mIsDoubleSided : 1;
bool mIsDefaultInstance : 1;
bool mUseUboBatching : 1;
TransparencyMode mTransparencyMode : 2;
uint64_t mMaterialSortingKey = 0;

View File

@@ -459,6 +459,13 @@ utils::Status MaterialParser::processTemplateSubstitutions(
utils::Status MaterialParser::parse(filamat::MaterialBuilder& builder,
const Config& config, ssize_t& size, std::unique_ptr<const char[]>& buffer) {
if (builder.getFeatureLevel() > config.getFeatureLevel()) {
utils::io::sstream errorMessage;
errorMessage << "Material feature level (" << +builder.getFeatureLevel()
<< ") is higher than maximum allowed (" << +config.getFeatureLevel() << ")";
return utils::Status::invalidArgument(errorMessage.c_str());
}
// Before attempting an expensive lex, let's find out if we were sent pure JSON.
utils::Status parsedStatus;
if (utils::Status validJson = isValidJsonStart(buffer.get(), size_t(size));
@@ -472,13 +479,6 @@ utils::Status MaterialParser::parse(filamat::MaterialBuilder& builder,
return parsedStatus;
}
if (builder.getFeatureLevel() > config.getFeatureLevel()) {
utils::io::sstream errorMessage;
errorMessage << "Material feature level (" << +builder.getFeatureLevel()
<< ") is higher than maximum allowed (" << +config.getFeatureLevel() << ")";
return utils::Status::invalidArgument(errorMessage.c_str());
}
switch (config.getReflectionTarget()) {
case Config::Metadata::NONE:
break;

View File

@@ -1,6 +1,6 @@
# Rendering Difference Test
This tool is a collections of scripts to run `gltf_viewer` and produce headless renderings.
We created a few scripts to run `gltf_viewer` and produce headless renderings.
This is mainly useful for continuous integration where GPUs are generally not available on cloud
machines. To perform software rasterization, these scripts are centered around [Mesa]'s
@@ -9,10 +9,10 @@ Additionally, we should be able to use GPUs where available (though this is more
work).
The script `render.py` contains the core logic for taking input parameters (such as the test
description file) and then running `gltf_viewer` to produce the renderings.
description file) and then running gltf_viewer to produce the renderings.
In the `test` directory is a list of test descriptions that are specified in json. Please see
`sample.json` to glean the structure.
`sample.json` to parse the structure.
## Setting up python

View File

@@ -229,7 +229,7 @@ if __name__ == '__main__':
important_print(f'Successfully compared {success_count} / {len(results)} images')
if tolerance_used_details:
pstr = 'Passed:'
pstr = 'Tolerance-based passes:'
for detail in tolerance_used_details:
pstr += '\n' + detail
important_print(pstr)
@@ -237,7 +237,7 @@ if __name__ == '__main__':
if failed_details:
pstr = 'Failed:'
for detail in failed_details:
pstr += '\n' + detail
pstr = '\n' + detail
important_print(pstr)
if len(failed) > 0:
exit(1)

View File

@@ -74,7 +74,7 @@ def _get_deletes_updates(update_dir, golden_dir):
for fpath in base.intersection(new):
base_fpath = os.path.join(golden_dir, fpath)
new_fpath = os.path.join(update_dir, fpath)
if (ext == 'tif' and not same_image(new_fpath, base_fpath)[0]) or \
if (ext == 'tif' and not same_image(new_fpath, base_fpath)) or \
(ext == 'json' and _file_as_str(new_fpath) != _file_as_str(base_fpath)):
update.append(fpath)

View File

@@ -1,13 +1,12 @@
AlphaBlendModeTest
AttenuationTest
BoomBoxWithAxes
Box
BoomBoxWithAxes
BoxInterleaved
BoxTextured
BoxTexturedNonPowerOfTwo
Duck
IridescenceSuzanne
IridescentDishWithOlives
Lantern
MetalRoughSpheres
NegativeScaleTest
@@ -18,5 +17,4 @@ Sponza
Suzanne
TextureCoordinateTest
TextureSettingsTest
TransmissionRoughnessTest
TwoSidedPlane

View File

@@ -5,9 +5,9 @@
"presets": [
{
"name": "base",
"models": ["Box", "BoxTextured", "FlightHelmet", "lucy"],
"models": ["Box", "BoxTextured", "Duck", "lucy", "FlightHelmet"],
"rendering": {
"viewer.cameraFocalLength": 35.0,
"viewer.cameraFocusDistance": 0,
"view.postProcessingEnabled": true,
"view.dithering": "NONE"
},
@@ -17,54 +17,27 @@
}
},
{
"name": "bloom_models",
"name": "helmet_only",
"models": ["DamagedHelmet"],
"rendering": {}
},
{
"name": "ssao_models",
"models": ["Duck", "lucy"],
"rendering": {}
},
{
"name": "transmission_models",
"models": ["TransmissionRoughnessTest", "IridescentDishWithOlives"],
"rendering": {}
}
],
"tests": [
{
"name": "Bloom",
"description": "Bloom",
"apply_presets": ["base", "bloom_models"],
"description": "Testing bloom",
"apply_presets": ["base", "helmet_only"],
"rendering": {
"view.bloom.enabled": true
}
},
{
"name": "MSAA",
"description": "Multisampling anti-aliasing",
"description": "Testing multisampling anti-aliasing",
"apply_presets": ["base"],
"rendering": {
"view.msaa.enabled": true
}
},
{
"name": "Transimssion",
"description": "transmission",
"apply_presets": ["base", "transmission_models"],
"rendering": {
"viewer.cameraFocalLength": 52.0,
"view.screenSpaceReflections.enabled": true
}
},
{
"name": "SSAO",
"description": "Screen space ambient occulsion",
"apply_presets": ["base", "ssao_models"],
"rendering": {
"view.ssao.enabled": true
}
}
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
To update the Perfetto library, run the following command from the tnt directory:
```shell
./update_perfetto.sh <version_tag>
```
For example:
```shell
./update_perfetto.sh v51.2
```
**Useful links:**
https://perfetto.dev/
https://perfetto.dev/docs/instrumentation/tracing-sdk

8
third_party/perfetto/tnt/README.txt vendored Normal file
View File

@@ -0,0 +1,8 @@
Useful links:
https://perfetto.dev/
https://perfetto.dev/docs/instrumentation/tracing-sdk
Perfetto was fetched as:
git clone https://github.com/google/perfetto.git -b v50.1
Only the sdk/ directory is needed.

View File

@@ -1,86 +0,0 @@
#!/bin/bash
set -euo pipefail
function usage() {
echo "Usage: $0 [-h] <version_tag>"
echo " -h: Display this help message"
echo " <version_tag>: The Perfetto version to download (e.g., v50.1)"
exit 1
}
if [[ $# -ne 1 || "$1" == "-h" ]]; then
usage
fi
DOWNLOAD_REF=$1
DOWNLOAD_URL="https://github.com/google/perfetto/archive/refs/tags/${DOWNLOAD_REF}.zip"
ZIP_FILE_NAME="${DOWNLOAD_REF}.zip"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
THIRD_PARTY_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
if ! command -v curl &> /dev/null; then
echo "Error: curl is not installed." >&2
exit 1
fi
if ! command -v unzip &> /dev/null; then
echo "Error: unzip is not installed." >&2
exit 1
fi
if ! command -v rsync &> /dev/null; then
echo "Error: rsync is not installed." >&2
exit 1
fi
cd "${THIRD_PARTY_DIR}"
echo "Downloading Perfetto ${DOWNLOAD_REF}..."
if ! curl -f -L -o "${ZIP_FILE_NAME}" "${DOWNLOAD_URL}"; then
echo "Error: Failed to download Perfetto ${DOWNLOAD_REF}." >&2
echo "Please check the version number and your network connection." >&2
rm -f "${ZIP_FILE_NAME}"
exit 1
fi
echo "Unzipping..."
TEMP_DIR="perfetto_temp_unzip"
# Clean up temp dir from previous runs, if any.
rm -rf "${TEMP_DIR}"
mkdir "${TEMP_DIR}"
if ! unzip -q "${ZIP_FILE_NAME}" -d "${TEMP_DIR}"; then
echo "Error: Failed to unzip the downloaded file." >&2
rm -f "${ZIP_FILE_NAME}"
rm -rf "${TEMP_DIR}"
exit 1
fi
# The archive contains a single directory. Find it.
EXTRACTED_DIR=$(find "${TEMP_DIR}" -mindepth 1 -maxdepth 1 -type d)
if [ -z "${EXTRACTED_DIR}" ] || [ ! -d "${EXTRACTED_DIR}" ]; then
echo "Error: Could not find the extracted directory inside the archive." >&2
rm -f "${ZIP_FILE_NAME}"
rm -rf "${TEMP_DIR}"
exit 1
fi
echo "Replacing existing perfetto contents with new version..."
# If perfetto_new exists, remove it.
rm -rf perfetto_new
mv "${EXTRACTED_DIR}" perfetto_new
# The perfetto directory should contain the content of the sdk directory from the archive, and our tnt directory.
rsync -a --delete perfetto_new/sdk/ perfetto/perfetto/
echo "Cleaning up..."
rm -rf "${ZIP_FILE_NAME}" perfetto_new "${TEMP_DIR}"
echo "Staging changes..."
git add perfetto
echo "Done. Please commit the changes."