Merge branch 'master' into ollielo-master-patch-72902

This commit is contained in:
Li-Ta Lo 2021-10-28 08:06:40 -06:00
commit b01ba506cf
78 changed files with 1604 additions and 980 deletions

@ -105,16 +105,36 @@
extends:
- .docker_image
.only-default: &only-default
only:
- master@vtk/vtk-m
- tags@vtk/vtk-m
- merge_requests
.ubuntu2004_hip_kokkos: &ubuntu2004_hip_kokkos
image: "kitware/vtkm:ci-ubuntu2004_hip_kokkos-20210827"
extends:
- .docker_image
.only-master: &only-master
only:
- master@vtk/vtk-m
.run_automatically: &run_automatically
rules:
- if: '$CI_MERGE_REQUEST_ID'
when: on_success
- if: '$CI_PROJECT_PATH == "vtk/vtk-m"'
when: on_success
- when: never
.run_scheduled: &run_scheduled
rules:
- if: '$CI_MERGE_REQUEST_ID'
when: manual
- if: '$CI_PROJECT_PATH == "vtk/vtk-m" && $CI_COMMIT_TAG'
when: on_success
- if: '$CI_PROJECT_PATH == "vtk/vtk-m" && $CI_PIPELINE_SOURCE == "schedule"'
when: on_success
- if: '$CI_PROJECT_PATH == "vtk/vtk-m"'
when: manual
- when: never
.run_master: &run_master
rules:
- if: '$CI_PROJECT_PATH == "vtk/vtk-m" && $CI_COMMIT_BRANCH == "master"'
when: on_success
- when: never
# General Longer Term Tasks:
# - Setup clang tidy as sub-pipeline
@ -189,7 +209,7 @@ stages:
before_script:
- *install_cmake
script:
- "ctest -VV -S .gitlab/ci/ctest_test.cmake"
- "ctest $CTEST_TIMEOUT -VV -S .gitlab/ci/ctest_test.cmake"
artifacts:
expire_in: 24 hours
when: always

@ -12,7 +12,7 @@ build:centos7_gcc73:
extends:
- .centos7
- .cmake_build_linux
- .only-default
- .run_automatically
- .use_minimum_supported_cmake
variables:
CMAKE_BUILD_TYPE: RelWithDebInfo
@ -30,7 +30,7 @@ test:centos7_gcc73:
extends:
- .centos7
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:centos7_gcc73
needs:
@ -47,7 +47,7 @@ test:rhel8_test_centos7:
extends:
- .rhel8
- .cmake_test_linux
- .only-default
- .run_automatically
variables:
CTEST_EXCLUSIONS: "built_against_test_install"
dependencies:

@ -10,7 +10,7 @@ build:centos8_sanitizer:
extends:
- .centos8
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_BUILD_TYPE: RelWithDebInfo
CMAKE_GENERATOR: "Unix Makefiles"
@ -26,7 +26,7 @@ test:centos8_sanitizer:
extends:
- .centos8
- .cmake_memcheck_linux
- .only-default
- .run_automatically
variables:
OMP_NUM_THREADS: 4
CTEST_MEMORYCHECK_TYPE: LeakSanitizer

@ -63,11 +63,25 @@ if("$ENV{VTKM_CI_NIGHTLY}" STREQUAL "TRUE")
set(CTEST_TRACK "Nightly")
endif()
if (CTEST_CMAKE_GENERATOR STREQUAL "Unix Makefiles")
# In Make, default parallelism to number of cores.
if(CTEST_CMAKE_GENERATOR STREQUAL "Unix Makefiles")
include(ProcessorCount)
ProcessorCount(nproc)
if(DEFINED ENV{CTEST_MAX_PARALLELISM})
if(nproc GREATER $ENV{CTEST_MAX_PARALLELISM})
set(nproc $ENV{CTEST_MAX_PARALLELISM})
endif()
endif()
set(CTEST_BUILD_FLAGS "-j${nproc}")
endif ()
endif()
# In Ninja, we do not need to specify parallelism unless we need to restrict
# the number of threads.
if(CTEST_CMAKE_GENERATOR STREQUAL "Ninja" AND DEFINED ENV{CTEST_MAX_PARALLELISM})
set(CTEST_BUILD_FLAGS "-j$ENV{CTEST_MAX_PARALLELISM}")
endif()
if(DEFINED ENV{CTEST_MEMORYCHECK_TYPE})
set(env_value "$ENV{CTEST_MEMORYCHECK_TYPE}")

@ -56,6 +56,9 @@ foreach(option IN LISTS options)
elseif(use_virtuals STREQUAL option)
set(VTKm_NO_DEPRECATED_VIRTUAL "OFF" CACHE STRING "")
elseif(no_testing STREQUAL option)
set(VTKm_ENABLE_TESTING OFF CACHE BOOL "")
elseif(examples STREQUAL option)
set(VTKm_ENABLE_EXAMPLES "ON" CACHE STRING "")
@ -64,7 +67,7 @@ foreach(option IN LISTS options)
elseif(benchmarks STREQUAL option)
set(VTKm_ENABLE_BENCHMARKS "ON" CACHE STRING "")
set(ENV{CMAKE_PREFIX_PATH} "$ENV{HOME}/gbench")
set(ENV{CMAKE_PREFIX_PATH} "$ENV{CMAKE_PREFIX_PATH}:$ENV{HOME}/gbench")
elseif(mpi STREQUAL option)
set(VTKm_ENABLE_MPI "ON" CACHE STRING "")
@ -95,6 +98,16 @@ foreach(option IN LISTS options)
elseif(turing STREQUAL option)
set(VTKm_CUDA_Architecture "turing" CACHE STRING "")
elseif(hip STREQUAL option)
if(CMAKE_VERSION VERSION_LESS_EQUAL 3.20)
message(FATAL_ERROR "VTK-m requires cmake > 3.20 to enable HIP support")
endif()
set(CMAKE_C_COMPILER "/opt/rocm/llvm/bin/clang" CACHE FILEPATH "")
set(CMAKE_CXX_COMPILER "/opt/rocm/llvm/bin/clang++" CACHE FILEPATH "")
set(VTKm_ENABLE_KOKKOS_HIP ON CACHE STRING "")
set(CMAKE_HIP_ARCHITECTURES "gfx900" CACHE STRING "")
endif()
endforeach()
@ -109,6 +122,10 @@ if(SCCACHE_COMMAND)
set(CMAKE_C_COMPILER_LAUNCHER "${SCCACHE_COMMAND}" CACHE STRING "")
set(CMAKE_CXX_COMPILER_LAUNCHER "${SCCACHE_COMMAND}" CACHE STRING "")
if(DEFINED VTKm_ENABLE_KOKKOS_HIP)
set(CMAKE_HIP_COMPILER_LAUNCHER "${SCCACHE_COMMAND}" CACHE STRING "")
endif()
# Use VTKm_CUDA_Architecture to determine if we need CUDA sccache setup
# since this will also capture when kokkos is being used with CUDA backing
if(DEFINED VTKm_CUDA_Architecture)

@ -0,0 +1,43 @@
FROM rocm/dev-ubuntu-20.04
LABEL maintainer "Vicente Adolfo Bolea Sanchez<vicente.bolea@kitware.com>"
# Base dependencies for building VTK-m projects
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
cmake \
curl \
g++ \
git \
git-lfs \
libmpich-dev \
libomp-dev \
mpich \
ninja-build \
rsync \
ssh \
software-properties-common
# Need to run git-lfs install manually on ubuntu based images when using the
# system packaged version
RUN git-lfs install
# Provide CMake
ARG CMAKE_VERSION=3.21.1
RUN mkdir /opt/cmake/ && \
curl -L https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-Linux-x86_64.sh > cmake-$CMAKE_VERSION-Linux-x86_64.sh && \
sh cmake-$CMAKE_VERSION-Linux-x86_64.sh --prefix=/opt/cmake/ --exclude-subdir --skip-license && \
rm cmake-$CMAKE_VERSION-Linux-x86_64.sh && \
ln -s /opt/cmake/bin/ctest /opt/cmake/bin/ctest-latest
ENV PATH "/opt/cmake/bin:${PATH}"
ENV CMAKE_PREFIX_PATH "/opt/rocm/lib/cmake:/opt/rocm/lib:${CMAKE_PREFIX_PATH}"
ENV CMAKE_GENERATOR "Ninja"
# Build and install Kokkos
ARG KOKKOS_VERSION=3.4.01
COPY kokkos_cmake_config.cmake kokkos_cmake_config.cmake
RUN curl -L https://github.com/kokkos/kokkos/archive/refs/tags/$KOKKOS_VERSION.tar.gz | tar -xzf - && \
cmake -S kokkos-$KOKKOS_VERSION -B build -C kokkos_cmake_config.cmake && \
cmake --build build -v && \
sudo cmake --install build
RUN rm -rf build

@ -0,0 +1,21 @@
##============================================================================
## Copyright (c) Kitware, Inc.
## All rights reserved.
## See LICENSE.txt for details.
##
## This software is distributed WITHOUT ANY WARRANTY; without even
## the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
## PURPOSE. See the above copyright notice for more information.
##============================================================================
set(CMAKE_BUILD_TYPE "release" CACHE STRING "")
set(CMAKE_INSTALL_PREFIX /opt/kokkos CACHE PATH "")
set(CMAKE_C_COMPILER /opt/rocm/llvm/bin/clang CACHE FILEPATH "")
set(CMAKE_CXX_COMPILER /opt/rocm/llvm/bin/clang++ CACHE FILEPATH "")
set(CMAKE_CXX_STANDARD "14" CACHE STRING "")
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "")
set(Kokkos_ENABLE_SERIAL ON CACHE BOOL "")
set(Kokkos_ARCH_VEGA900 ON CACHE BOOL "")
set(Kokkos_ENABLE_HIP ON CACHE BOOL "")
set(Kokkos_ENABLE_HIP_RELOCATABLE_DEVICE_CODE OFF CACHE BOOL "")

@ -10,7 +10,7 @@ build:rhel8:
extends:
- .rhel8
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_GENERATOR: "Unix Makefiles"
VTKM_SETTINGS: "serial+shared+64bit_floats+32bit_ids+use_virtuals"
@ -24,7 +24,7 @@ test:rhel8:
extends:
- .rhel8
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:rhel8
needs:
@ -41,7 +41,7 @@ build:rhel8_vtk_types:
extends:
- .rhel8
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_GENERATOR: "Unix Makefiles"
VTKM_SETTINGS: "serial+vtk_types"
@ -55,7 +55,7 @@ test:rhel8_vtk_types:
extends:
- .rhel8
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:rhel8_vtk_types
needs:

@ -12,7 +12,7 @@ build:ubuntu1604_gcc5:
extends:
- .ubuntu1604_cuda
- .cmake_build_linux
- .only-default
- .run_automatically
- .use_minimum_supported_cmake
variables:
CC: "gcc-5"
@ -31,7 +31,7 @@ test:ubuntu1604_gcc5:
extends:
- .ubuntu1604_cuda
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu1604_gcc5
needs:
@ -50,7 +50,7 @@ build:ubuntu1604_gcc5_2:
extends:
- .ubuntu1604_cuda
- .cmake_build_linux
- .only-master
- .run_master
- .use_minimum_supported_cmake
variables:
CC: "gcc-5"
@ -69,7 +69,7 @@ test:ubuntu1804_test_ubuntu1604_gcc5_2:
extends:
- .ubuntu1804_cuda
- .cmake_test_linux
- .only-master
- .run_master
variables:
CTEST_EXCLUSIONS: "built_against_test_install"
dependencies:
@ -88,7 +88,7 @@ build:ubuntu1604_clang5:
extends:
- .ubuntu1604
- .cmake_build_linux
- .only-default
- .run_automatically
- .use_minimum_supported_cmake
variables:
CC: "clang-5.0"
@ -105,7 +105,7 @@ test:ubuntu1604_clang5:
extends:
- .ubuntu1604
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu1604_clang5
needs:

@ -11,7 +11,7 @@ build:ubuntu1804_gcc9:
extends:
- .ubuntu1804
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CC: "gcc-9"
CXX: "g++-9"
@ -27,7 +27,7 @@ test:ubuntu1804_gcc9:
extends:
- .ubuntu1804
- .cmake_test_linux
- .only-default
- .run_automatically
variables:
#Restrict OpenMP number of threads since multiple test stages
#execute on the same hardware concurrently
@ -51,7 +51,7 @@ build:ubuntu1804_gcc7:
extends:
- .ubuntu1804_cuda
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CC: "gcc-7"
CXX: "g++-7"
@ -69,7 +69,7 @@ test:ubuntu1804_gcc7:
extends:
- .ubuntu1804_cuda
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu1804_gcc7
needs:
@ -90,8 +90,8 @@ build:ubuntu1804_clang_cuda:
extends:
- .ubuntu1804_cuda
- .cmake_build_linux
- .only-default
# - .only-master
- .run_automatically
# - .run_master
variables:
CC: "clang-8"
CXX: "clang++-8"
@ -109,8 +109,8 @@ test:ubuntu1804_clang_cuda:
extends:
- .ubuntu1804_cuda
- .cmake_test_linux
- .only-default
# - .only-master
- .run_automatically
# - .run_master
dependencies:
- build:ubuntu1804_clang_cuda
needs:
@ -127,7 +127,7 @@ build:ubuntu1804_gcc6:
extends:
- .ubuntu1804
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CC: "gcc-6"
CXX: "g++-6"
@ -142,7 +142,7 @@ test:ubuntu1804_gcc6:
extends:
- .ubuntu1804
- .cmake_test_linux
- .only-default
- .run_automatically
variables:
#Restrict OpenMP number of threads since multiple test stages
#execute on the same hardware concurrently
@ -163,7 +163,7 @@ build:ubuntu1804_clang8:
extends:
- .ubuntu1804
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CC: "clang-8"
CXX: "clang++-8"
@ -179,7 +179,7 @@ test:ubuntu1804_clang8:
extends:
- .ubuntu1804
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu1804_clang8
needs:
@ -198,7 +198,7 @@ build:ubuntu1804_kokkos:
extends:
- .ubuntu1804_cuda_kokkos
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_GENERATOR: "Ninja"
CMAKE_BUILD_TYPE: Release
@ -215,7 +215,7 @@ test:ubuntu1804_kokkos:
extends:
- .ubuntu1804_cuda_kokkos
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu1804_kokkos
needs:

@ -7,7 +7,7 @@ build:ubuntu2004_gcc9:
extends:
- .ubuntu2004
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_BUILD_TYPE: Debug
VTKM_SETTINGS: "benchmarks+tbb+openmp+mpi+shared+hdf5"
@ -21,7 +21,7 @@ test:ubuntu2004_gcc9:
extends:
- .ubuntu2004
- .cmake_test_linux
- .only-default
- .run_automatically
variables:
#Restrict OpenMP number of threads since multiple test stages
#execute on the same hardware concurrently
@ -40,7 +40,7 @@ build:ubuntu2004_kokkos:
extends:
- .ubuntu2004_kokkos
- .cmake_build_linux
- .only-default
- .run_automatically
variables:
CMAKE_BUILD_TYPE: RelWithDebInfo
VTKM_SETTINGS: "kokkos+shared+64bit_floats"
@ -54,8 +54,43 @@ test:ubuntu2004_kokkos:
extends:
- .ubuntu2004_kokkos
- .cmake_test_linux
- .only-default
- .run_automatically
dependencies:
- build:ubuntu2004_kokkos
needs:
- build:ubuntu2004_kokkos
build:ubuntu2004_hip_kokkos:
tags:
- build
- vtkm
- docker
- radeon
extends:
- .ubuntu2004_hip_kokkos
- .cmake_build_linux
- .run_scheduled
variables:
CMAKE_BUILD_TYPE: RelWithDebInfo
VTKM_SETTINGS: "benchmarks+kokkos+hip+no_virtual+no_rendering"
CMAKE_PREFIX_PATH: "/opt/rocm/lib/cmake"
CTEST_MAX_PARALLELISM: "1"
timeout: 12 hours
test:ubuntu2004_hip_kokkos:
tags:
- build
- vtkm
- docker
- radeon
extends:
- .ubuntu2004_hip_kokkos
- .cmake_test_linux
- .run_automatically
variables:
CTEST_TIMEOUT: "30"
dependencies:
- build:ubuntu2004_hip_kokkos
needs:
- build:ubuntu2004_hip_kokkos
timeout: 3 hours

@ -93,7 +93,7 @@ build:windows_vs2019:
- large-memory
extends:
- .cmake_build_windows
- .only-default
- .run_automatically
variables:
CMAKE_GENERATOR: "Ninja"
CMAKE_BUILD_TYPE: Release
@ -114,7 +114,7 @@ test:windows_vs2019:
- turing
extends:
- .cmake_test_windows
- .only-default
- .run_automatically
dependencies:
- build:windows_vs2019
needs:

@ -1,4 +1,4 @@
VTKm License Version 1.6
VTKm License Version 1.7
========================================================================
Copyright (c) 2014-2021

@ -200,9 +200,9 @@ def create_container(ci_file_path, *args):
script_search_locations = [ci_state, subset, runner]
for loc in script_search_locations:
if 'before_script' in loc:
before_script = loc['before_script']
before_script = [command.strip() for command in loc['before_script']]
if 'script' in loc:
script = loc['script']
script = [command.strip() for command in loc['script']]
docker_template = string.Template('''
FROM $image
@ -223,10 +223,10 @@ RUN echo "$before_script" >> /setup-gitlab-env.sh && \
job_name='local-build'+runner_name,
src_dir=src_dir,
gitlab_env= " ".join(gitlab_env),
before_script="\n".join(before_script)
.replace('\n', '\\n\\\n')
before_script=" && ".join(before_script)
.replace('\n', ' && ')
.replace('"', '\\"'),
script="\n".join(script).replace('\n', '\\n\\\n')
script=" && ".join(script).replace('\n', ' && ')
.replace('"', '\\"'))
# Write out the file

@ -10,7 +10,7 @@
#ifndef vtk_m_benchmarking_Benchmarker_h
#define vtk_m_benchmarking_Benchmarker_h
#include <sstream>
#include <vtkm/cont/Initialize.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>

@ -0,0 +1,437 @@
VTK-m 1.7 Release Notes
=======================
# Table of Contents
1. [Core](#Core)
- Add ability to convert fields to known types
- Consolidate count-to-offset algorithms
- Make field names from sources more descriptive
2. [ArrayHandle](#ArrayHandle)
- Added `ArrayCopyShallowIfPossible`
- Added copy methods to `UnknownArrayHandle`
- Allow a `const ArrayHandle` to be reallocated
- Compile `ArrayGetValues` implementation in a library
- Deprecated `VariantArrayHandle`
- Improve type reporting in `UnknownArrayHandle`
[4](4). [Control Environment](#Control-Environment)
- Compile reverse connectivity builder into vtkm_cont library
5. [Execution Environment](#Execution-Environment)
- Remove unbounded recursion
6. [Worklets and Filters](#Worklets-and-Filters)
- [GenerateIds](GenerateIds) filter
- Support scatter/mask for CellSetExtrude
- Adding ability to use cell-centered velocity fields for particle advection
- Probe always generates point fields
7. [Build](#Build)
- Filters instantiation generator
- Move .cxx from DEVICE_SOURCES to SOURCES in vktm IO library
8. [Other](#Other)
- Enable `TypeToString` for `type_info`
- Skip library versions
- Support writing binary files to legacy VTK files
# Core
## Add ability to convert fields to known types ##
In VTK-m we have a constant tension between minimizing the number of
types we have to compile for (to reduce compile times and library size)
and maximizing the number of types that our filters support.
Unfortunately, if you don't compile a filter for a specific array type
(value type and storage), trying to run that filter will simply fail.
To compromise between the two, added methods to `DataSet` and `Field`
that will automatically convert the data in the `Field` arrays to a type
that VTK-m will understand. Although this will cause an extra data copy,
it will at least prevent the program from failing, and thus make it more
feasible to reduce types.
## Consolidate count-to-offset algorithms ##
For no particularly good reason, there were two functions that converted
and array of counts to an array of offsets: `ConvertNumComponentsToOffsets`
and `ConvertNumIndicesToOffsets`. These functions were identical, except
one was defined in `ArrayHandleGroupVecVariable.h` and the other was
defined in `CellSetExplicit.h`.
These two functions have been consolidated into one (which is now called
`ConvertNumComponentsToOffsets`). The consolidated function has also been
put in its own header file: `ConvertNumComponentsToOffsets.h`.
Normally, backward compatibility would be established using deprecated
features. However, one of the things being worked on is the removal of
device-specific code (e.g. `vtkm::cont::Algorithm`) from core classes like
`CellSetExplicit` so that less code needs to use the device compiler
(especially downstream code).
`ConvertNumComponentsToOffsets` has also been changed to provide a
pre-compiled version for common arrays. This helps with the dual goals of
compiling less device code and allowing data set builders to not have to
use the device compiler. For cases where you need to compile
`ConvertNumComponentsToOffsets` for a different kind of array, you can use
the internal `ConvertNumComponentsToOffsetsTemplate`.
Part of this change removed unnecessary includes of `Algorithm.h` in
`ArrayHandleGroupVecVariable.h` and `CellSetExplicit.h`. This header had to
be added to some classes that were not including it themselves.
## Make field names from sources more descriptive ##
The VTK-m sources (like `Oscillator`, `Tangle`, and `Wavelet`) were all
creating fields with very generic names like `pointvar` or `scalars`. These
are very unhelpful names as it is impossible for downstream processes to
identify the meaning of these fields. Imagine having these data saved to a
file and then a different person trying to identify what they mean. Or
imagine dealing with more than one such source at a time and trying to
manage fields with similar or overlapping names.
The following renames happened:
* `Oscillator`: `scalars` -> `oscillating`
* `Tangle`: `pointvar` -> `tangle`
* `Wavelet`: `scalars` -> `RTData` (matches VTK source)
# ArrayHandle
## Added `ArrayCopyShallowIfPossible` ##
Often times you have an array of an unknown type (likely from a data set),
and you need it to be of a particular type (or can make a reasonable but
uncertain assumption about it being a particular type). You really just
want a shallow copy (a reference in a concrete `ArrayHandle`) if that is
possible.
`ArrayCopyShallowIfPossible` pulls an array of a specific type from an
`UnknownArrayHandle`. If the type is compatible, it will perform a shallow
copy. If it is not possible, a deep copy is performed to get it to the
correct type.
## Added copy methods to `UnknownArrayHandle` ##
`vtkm::cont::UnknownArrayHandle` now provides a set of method that allows
you to copy data from one `UnknownArrayHandle` to another. The first
method, `DeepCopyFrom`, takes a source `UnknownArrayHandle` and deep copies
the data to the called one. If the `UnknownArrayHandle` already points to a
real `ArrayHandle`, the data is copied into that `ArrayHandle`. If the
`UnknownArrayHandle` does not point to an existing `ArrayHandle`, then a
new `ArrayHandleBasic` with the same value type as the source is created
and copied into.
The second method, `CopyShallowIfPossibleFrom` behaves similarly to
`DeepCopyFrom` except that it will perform a shallow copy if possible. That
is, if the target `UnknownArrayHandle` points to an `ArrayHandle` of the
same type as the source `UnknownArrayHandle`, then a shallow copy occurs
and the underlying `ArrayHandle` will point to the source. If the types
differ, then a deep copy is performed. If the target `UnknownArrayHandle`
does not point to an `ArrayHandle`, then the behavior is the same as the
`=` operator.
One of the intentions of these new methods is to allow you to copy arrays
without using a device compiler (e.g. `nvcc`). Calling `ArrayCopy` requires
you to include the `ArrayCopy.h` header file, and that in turn requires
device adapter algorithms. These methods insulate you from these.
## Allow a `const ArrayHandle` to be reallocated ##
Previously, the `Allocate` method of `ArrayHandle` was _not_ declared as
`const`. Likewise, the methods that depended on `Allocate`, namely
`ReleaseResources` and `PrepareForOutput` were also not declared `const`.
The main consequence of this was that if an `ArrayHandle` were passed as a
constant reference argument to a method (e.g. `const ArrayHandle<T>& arg`),
then the array could not be reallocated.
This seems right at first blush. However, we have changed these methods to
be `const` so that you can in fact reallocate the `ArrayHandle`. This is
because the `ArrayHandle` is in principle a pointer to an array pointer.
Such a structure in C will allow you to change the pointer to the array,
and so in this context it makes sense for `ArrayHandle` to support that as
well.
Although this distinction will certainly be confusing to users, we think
this change is correct for a variety of reasons.
1. This change makes the behavior of `ArrayHandle` consistent with the
behavior of `UnknownArrayHandle`. The latter needed this behavior to
allow `ArrayHandle`s to be passed as output arguments to methods that
get automatically converted to `UnknownArrayHandle`.
2. Before this change, a `const ArrayHandle&` was still multible is many
way. In particular, it was possible to change the data in the array
even if the array could not be resized. You could still call things
like `WritePortal` and `PrepareForInOut`. The fact that you could
change it for some things and not others was confusing. The fact that
you could call `PrepareForInOut` but not `PrepareForOutput` was doubly
confusing.
3. Passing a value by constant reference should be the same, from the
calling code's perspective, as passing by value. Although the function
can change an argument passed by value, that change is not propogated
back to the calling code. However, in the case of `ArrayHandle`,
calling by value would allow the array to be reallocated from the
calling side whereas a constant reference would prevent that. This
change makes the two behaviors consistent.
4. The supposed assurance that the `ArrayHandle` would not be reallocated
was easy to break even accidentally. If the `ArrayHandle` was assigned
to another `ArrayHandle` (for example as a class' member or wrapped
inside of an `UnknownArrayHandle`), then the array was free to be
reallocated.
## Compile `ArrayGetValues` implementation in a library ##
Previously, all of the `ArrayGetValue` implementations were templated
functions that had to be built by all code that used it. That had 2
negative consequences.
1. The same code that scheduled jobs on any device had to be compiled many
times over.
2. Any code that used `ArrayGetValue` had to be compiled with a device
compiler. If you had non-worklet code that just wanted to get a single
value out of an array, that was a pain.
To get around this problem, an `ArrayGetValues` function that takes
`UnknownArrayHandle`s was created. The implementation for this function is
compiled into a library. It uses `UnknownArrayHandle`'s ability to extract
a component of the array with a uniform type to reduce the number of code
paths it generates. Although there are still several code paths, they only
have to be computed once. Plus, now any code can include `ArrayGetValues.h`
and still use a basic C++ compiler.
## Deprecated `VariantArrayHandle` ##
`VaraintArrayHandle` has been replaced by `UnknownArrayHandle` and
`UncertainArrayHandle`. Officially made `VariantArrayHandle` deprecated and
point users to the new implementations.
## Improve type reporting in `UnknownArrayHandle` ##
Added features with reporting types with `UnknownArrayHandle`. First, added
a method named `GetArrayTypeName` that returns a string containing the type
of the contained array. There were already methods `GetValueType` and
`GetStorageType`, but this provides a convenience to get the whole name in
one go.
Also improved the reporting when an `AsArrayHandle` call failed. Before,
the thrown method just reported that the `UnknownArrayHandle` could not be
converted to the given type. Now, it also reports the type actually held by
the `UnknownArrayHandle` so the user can better understand why the
conversion failed.
# Control Environment
## Compile reverse connectivity builder into vtkm_cont library ##
Because `CellSetExplicit` is a templated class, the implementation of
most of its features is part of the header files. One of the things that
was included was the code to build the reverse connectivity links. That
is, it figured out which cells were incident on each point using the
standard connections of which points comprise which cells.
Of course, building these links is non-trivial, and it used multiple
DPPs to engage the device. It meant that header had to include the
device adapter algorithms and therefore required a device compiler. We
want to minimize this where possible.
To get around this issue, a non-templated function was added to find the
reverse connections of a `CellSetExplicit`. It does this by passing in
`UnknownArrayHandle`s for the input arrays. (The output visit-points-
with-cells arrays are standard across all template instances.) The
implementation first iterates over all `CellSetExplicit` versions in
`VTKM_DEFAULT_CELL_SETS` and attempts to retrieve arrays of those types.
In the unlikely event that none of these arrays work, it copies the data
to `ArrayHandle<vtkm::Id>` and uses those.
# Execution Environment
## Remove unbounded recursion ##
GPU device compilers like to determine the stack size needed for a called
kernel. This is only possible if there is no recursive function calls on
the device or at least recursive calls where the termination cannot be
found at compile time.
Device compilers do not particularly like that. We have been getting around
this with CUDA by turning of warnings about stack sizes and setting a large
stack size during a call (which works but is dangerous). More restrictive
devices might not allow recursive calls at all.
To fix this, we will avoid recursive calls in execution environment
(device) code. All such warnings are turned on.
Because of this, we also should not have to worry about lengthening the
stack size, so that code is also removed.
# Worklets and Filters
## GenerateIds filter ##
This filter adds a pair of fields to a `DataSet` which mirror the
indices of the points and cells, respectively. These fields are useful
for tracking the provenance of the elements of a `DataSet` as it gets
manipulated by the filters. It is also convenient for adding indices to
operations designed for fields and for testing purposes.
## Support scatter/mask for CellSetExtrude ##
Scheduling topology map workets for `CellSetExtrude` always worked, but the
there were indexing problems when a `Scatter` or a `Mask` was used. This
has been corrected, and now `Scatter`s and `Mask`s are supported on
topology maps on `CellSetExtrude`.
## Adding ability to use cell-centered velocity fields for particle advection ##
Vector fields for particle advection are not always nodal,; e.g., AMR-Wind uses
zonal vector fields to store velocity information. Previously, VTK-m filters
only supported particle advection in nodal vector fields. With this change, VTK-m
will support zonal vector fields. Users do not need to worry about changing the
way they specify inputs to the flow visualization filters. However, if users use
the particle advection worklets, they'll need to specify the associativity for
their vector fields.
## Probe always generates point fields ##
Previously, the `probe` filter, when probing the input's cell fields, would
store the result as the output's cell field. This is a bug since the probing is
done at the geometry's point locations, and the output gets its structure from
the `geometry`.
This behaviour is fixed in this release. Now, irrespective of the type of the
input field being probed, the result field is always a point field.
```
vtkm::cont::Field field = dataset.GetField("velocity");
vtkm::cont::Field::Association assoc = field.GetAssociation();
using FieldArray = vtkm::cont::ArrayHandle<vtkm::Vec3f>;
using FieldType = vtkm::worklet::particleadvection::VelocityField<FieldType>;
FieldArray data;
field.GetData().AsArrayHandle<FieldArray>(data);
// Use this field to pass to the GridEvaluators
FieldType velocities(data, assoc);
```
# Build
## Filters instantiation generator ##
It introduces a template instantiation generator. This aims to significantly
reduce the memory usage when building _VTK-m_ filter by effectively splitting
templates instantiations across multiple files.
How this works revolves around automatically instantiating filters template
methods inside transient instantiation files which resides solely in the build
directory. Each of those transient files contains a single explicit template
instantiation.
Here is an example of how to produce an instantiation file.
First, at the filter header file:
```c++
// 1. Include Instantiations header
#include <vtkm/filter/Instantiations.h>
class Contour {
template <typename T, typename StorageType, typename DerivedPolicy>
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<T, StorageType>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<DerivedPolicy>);
};
// 2. Create extern template instantiation and surround with
// VTKM_INSTANTIATION_{BEGIN,END}
VTKM_INSTANTIATION_BEGIN
extern template vtkm::cont::DataSet Contour::DoExecute(
const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<vtkm::UInt8>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<vtkm::filter::PolicyDefault>);
VTKM_INSTANTIATION_END
```
Later, in its corresponding `CMakeLists.txt` file:
```cmake
vtkm_add_instantiations(ContourInstantiations FILTER Contour)
vtkm_library(
NAME vtkm_filter_contour
...
DEVICE_SOURCES ${ContourInstantiations}
)
```
After running the configure step in _CMake_, this will result in the creation of
the following transient file in the build directory:
```c++
#ifndef vtkm_filter_ContourInstantiation0_cxx
#define vtkm_filter_ContourInstantiation0_cxx
#endif
/* Needed for linking errors when no instantiations */
int __vtkm_filter_ContourInstantiation0_cxx;
#include <vtkm/filter/Contour.h>
#include <vtkm/filter/Contour.hxx>
namespace vtkm
{
namespace filter
{
template vtkm::cont::DataSet Contour::DoExecute(
const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<vtkm::UInt8>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<vtkm::filter::PolicyDefault>);
}
}
#undef vtkm_filter_ContourInstantiation0_cxx
```
## Move .cxx from DEVICE_SOURCES to SOURCES in vktm io library ##
It used to be that every .cxx file which uses ArrayHandle needs to be compiled
by the device compiler. A recent change had removed this restriction.
One exception is that user of ArrayCopy still requires device compiler.
Since most .cxx files in vtkm/io do not use ArrayCopy, they are moved
to SOURCES and are compiled by host compiler.
# Other
## Enable `TypeToString` for `type_info` ##
VTK-m contains a helpful method named `vtkm::cont::TypeToString` that
either takes a type as a template argument or a `std::type_info` object and
returns a human-readable string for that type.
The standard C++ library has an alternate for `std::type_info` named
`std::type_index`, which has the added ability to be used in a container
like `set` or `map`. The `TypeToString` overloads have been extended to
also accept a `std::type_info` and report the name of the type stored in it
(rather than the name of `type_info` itself).
## skip library versions ##
The `VTKm_SKIP_LIBRARY_VERSIONS` variable is now available to skip the SONAME
and SOVERSION fields (or the equivalent for non-ELF platforms).
Some deployments (e.g., Python wheels or Java `.jar` files) do not support
symlinks reliably and the way the libraries get loaded just leads to
unnecessary files in the packaged artifact.
## Support writing binary files to legacy VTK files ##
The legacy VTK file writer writes out in ASCII. This is helpful when a
human is trying to read the file. However, if you have more than a
trivial amount of data, the file can get impractically large. To get
around this, `VTKDataSetWriter` now has a flag that allows you to write
the data in binary format.

@ -1,42 +0,0 @@
# Allow a `const ArrayHandle` to be reallocated
Previously, the `Allocate` method of `ArrayHandle` was _not_ declared as
`const`. Likewise, the methods that depended on `Allocate`, namely
`ReleaseResources` and `PrepareForOutput` were also not declared `const`.
The main consequence of this was that if an `ArrayHandle` were passed as a
constant reference argument to a method (e.g. `const ArrayHandle<T>& arg`),
then the array could not be reallocated.
This seems right at first blush. However, we have changed these methods to
be `const` so that you can in fact reallocate the `ArrayHandle`. This is
because the `ArrayHandle` is in principle a pointer to an array pointer.
Such a structure in C will allow you to change the pointer to the array,
and so in this context it makes sense for `ArrayHandle` to support that as
well.
Although this distinction will certainly be confusing to users, we think
this change is correct for a variety of reasons.
1. This change makes the behavior of `ArrayHandle` consistent with the
behavior of `UnknownArrayHandle`. The latter needed this behavior to
allow `ArrayHandle`s to be passed as output arguments to methods that
get automatically converted to `UnknownArrayHandle`.
2. Before this change, a `const ArrayHandle&` was still multible is many
way. In particular, it was possible to change the data in the array
even if the array could not be resized. You could still call things
like `WritePortal` and `PrepareForInOut`. The fact that you could
change it for some things and not others was confusing. The fact that
you could call `PrepareForInOut` but not `PrepareForOutput` was doubly
confusing.
3. Passing a value by constant reference should be the same, from the
calling code's perspective, as passing by value. Although the function
can change an argument passed by value, that change is not propogated
back to the calling code. However, in the case of `ArrayHandle`,
calling by value would allow the array to be reallocated from the
calling side whereas a constant reference would prevent that. This
change makes the two behaviors consistent.
4. The supposed assurance that the `ArrayHandle` would not be reallocated
was easy to break even accidentally. If the `ArrayHandle` was assigned
to another `ArrayHandle` (for example as a class' member or wrapped
inside of an `UnknownArrayHandle`), then the array was free to be
reallocated.

@ -1,14 +0,0 @@
# Improve type reporting in `UnknownArrayHandle`
Added features with reporting types with `UnknownArrayHandle`. First, added
a method named `GetArrayTypeName` that returns a string containing the type
of the contained array. There were already methods `GetValueType` and
`GetStorageType`, but this provides a convenience to get the whole name in
one go.
Also improved the reporting when an `AsArrayHandle` call failed. Before,
the thrown method just reported that the `UnknownArrayHandle` could not be
converted to the given type. Now, it also reports the type actually held by
the `UnknownArrayHandle` so the user can better understand why the
conversion failed.

@ -1,28 +0,0 @@
# Consolidate count-to-offset algorithms
For no particularly good reason, there were two functions that converted
and array of counts to an array of offsets: `ConvertNumComponentsToOffsets`
and `ConvertNumIndicesToOffsets`. These functions were identical, except
one was defined in `ArrayHandleGroupVecVariable.h` and the other was
defined in `CellSetExplicit.h`.
These two functions have been consolidated into one (which is now called
`ConvertNumComponentsToOffsets`). The consolidated function has also been
put in its own header file: `ConvertNumComponentsToOffsets.h`.
Normally, backward compatibility would be established using deprecated
features. However, one of the things being worked on is the removal of
device-specific code (e.g. `vtkm::cont::Algorithm`) from core classes like
`CellSetExplicit` so that less code needs to use the device compiler
(especially downstream code).
`ConvertNumComponentsToOffsets` has also been changed to provide a
pre-compiled version for common arrays. This helps with the dual goals of
compiling less device code and allowing data set builders to not have to
use the device compiler. For cases where you need to compile
`ConvertNumComponentsToOffsets` for a different kind of array, you can use
the internal `ConvertNumComponentsToOffsetsTemplate`.
Part of this change removed unnecessary includes of `Algorithm.h` in
`ArrayHandleGroupVecVariable.h` and `CellSetExplicit.h`. This header had to
be added to some classes that were not including it themselves.

@ -1,13 +0,0 @@
# Add ability to convert fields to known types
In VTK-m we have a constant tension between minimizing the number of
types we have to compile for (to reduce compile times and library size)
and maximizing the number of types that our filters support.
Unfortunately, if you don't compile a filter for a specific array type
(value type and storage), trying to run that filter will simply fail.
To compromise between the two, added methods to `DataSet` and `Field`
that will automatically convert the data in the `Field` arrays to a type
that VTK-m will understand. Although this will cause an extra data copy,
it will at least prevent the program from failing, and thus make it more
feasible to reduce types.

@ -1,5 +0,0 @@
# Deprecated `VariantArrayHandle`
`VaraintArrayHandle` has been replaced by `UnknownArrayHandle` and
`UncertainArrayHandle`. Officially made `VariantArrayHandle` deprecated and
point users to the new implementations.

@ -1,85 +0,0 @@
# Filters instantiation generator
It introduces a template instantiation generator. This aims to significantly
reduce the memory usage when building _VTK-m_ filter by effectively splitting
templates instantiations across multiple files.
How this works revolves around automatically instantiating filters template
methods inside transient instantiation files which resides solely in the build
directory. Each of those transient files contains a single explicit template
instantiation.
Here is an example of how to produce an instantiation file.
First, at the filter header file:
```c++
// 1. Include Instantiations header
#include <vtkm/filter/Instantiations.h>
class Contour {
template <typename T, typename StorageType, typename DerivedPolicy>
vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<T, StorageType>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<DerivedPolicy>);
};
// 2. Create extern template instantiation and surround with
// VTKM_INSTANTIATION_{BEGIN,END}
VTKM_INSTANTIATION_BEGIN
extern template vtkm::cont::DataSet Contour::DoExecute(
const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<vtkm::UInt8>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<vtkm::filter::PolicyDefault>);
VTKM_INSTANTIATION_END
```
Later, in its corresponding `CMakeLists.txt` file:
```cmake
vtkm_add_instantiations(ContourInstantiations FILTER Contour)
vtkm_library(
NAME vtkm_filter_contour
...
DEVICE_SOURCES ${ContourInstantiations}
)
```
After running the configure step in _CMake_, this will result in the creation of
the following transient file in the build directory:
```c++
#ifndef vtkm_filter_ContourInstantiation0_cxx
#define vtkm_filter_ContourInstantiation0_cxx
#endif
/* Needed for linking errors when no instantiations */
int __vtkm_filter_ContourInstantiation0_cxx;
#include <vtkm/filter/Contour.h>
#include <vtkm/filter/Contour.hxx>
namespace vtkm
{
namespace filter
{
template vtkm::cont::DataSet Contour::DoExecute(
const vtkm::cont::DataSet&,
const vtkm::cont::ArrayHandle<vtkm::UInt8>&,
const vtkm::filter::FieldMetadata&,
vtkm::filter::PolicyBase<vtkm::filter::PolicyDefault>);
}
}
#undef vtkm_filter_ContourInstantiation0_cxx
```

@ -1,7 +0,0 @@
# GenerateIds filter
This filter adds a pair of fields to a `DataSet` which mirror the
indices of the points and cells, respectively. These fields are useful
for tracking the provenance of the elements of a `DataSet` as it gets
manipulated by the filters. It is also convenient for adding indices to
operations designed for fields and for testing purposes.

@ -1,17 +0,0 @@
# Remove unbounded recursion
GPU device compilers like to determine the stack size needed for a called
kernel. This is only possible if there is no recursive function calls on
the device or at least recursive calls where the termination cannot be
found at compile time.
Device compilers do not particularly like that. We have been getting around
this with CUDA by turning of warnings about stack sizes and setting a large
stack size during a call (which works but is dangerous). More restrictive
devices might not allow recursive calls at all.
To fix this, we will avoid recursive calls in execution environment
(device) code. All such warnings are turned on.
Because of this, we also should not have to worry about lengthening the
stack size, so that code is also removed.

@ -1,20 +0,0 @@
# Compile `ArrayGetValues` implementation in a library
Previously, all of the `ArrayGetValue` implementations were templated
functions that had to be built by all code that used it. That had 2
negative consequences.
1. The same code that scheduled jobs on any device had to be compiled many
times over.
2. Any code that used `ArrayGetValue` had to be compiled with a device
compiler. If you had non-worklet code that just wanted to get a single
value out of an array, that was a pain.
To get around this problem, an `ArrayGetValues` function that takes
`UnknownArrayHandle`s was created. The implementation for this function is
compiled into a library. It uses `UnknownArrayHandle`'s ability to extract
a component of the array with a uniform type to reduce the number of code
paths it generates. Although there are still several code paths, they only
have to be computed once. Plus, now any code can include `ArrayGetValues.h`
and still use a basic C++ compiler.

@ -1,21 +0,0 @@
# Compile reverse connectivity builder into vtkm_cont library
Because `CellSetExplicit` is a templated class, the implementation of
most of its features is part of the header files. One of the things that
was included was the code to build the reverse connectivity links. That
is, it figured out which cells were incident on each point using the
standard connections of which points comprise which cells.
Of course, building these links is non-trivial, and it used multiple
DPPs to engage the device. It meant that header had to include the
device adapter algorithms and therefore required a device compiler. We
want to minimize this where possible.
To get around this issue, a non-templated function was added to find the
reverse connections of a `CellSetExplicit`. It does this by passing in
`UnknownArrayHandle`s for the input arrays. (The output visit-points-
with-cells arrays are standard across all template instances.) The
implementation first iterates over all `CellSetExplicit` versions in
`VTKM_DEFAULT_CELL_SETS` and attempts to retrieve arrays of those types.
In the unlikely event that none of these arrays work, it copies the data
to `ArrayHandle<vtkm::Id>` and uses those.

@ -1,6 +0,0 @@
# Support scatter/mask for CellSetExtrude
Scheduling topology map workets for `CellSetExtrude` always worked, but the
there were indexing problems when a `Scatter` or a `Mask` was used. This
has been corrected, and now `Scatter`s and `Mask`s are supported on
topology maps on `CellSetExtrude`.

@ -1,12 +0,0 @@
# Added `ArrayCopyShallowIfPossible`
Often times you have an array of an unknown type (likely from a data set),
and you need it to be of a particular type (or can make a reasonable but
uncertain assumption about it being a particular type). You really just
want a shallow copy (a reference in a concrete `ArrayHandle`) if that is
possible.
`ArrayCopyShallowIfPossible` pulls an array of a specific type from an
`UnknownArrayHandle`. If the type is compatible, it will perform a shallow
copy. If it is not possible, a deep copy is performed to get it to the
correct type.

@ -1,8 +0,0 @@
# skip library versions
The `VTKm_SKIP_LIBRARY_VERSIONS` variable is now available to skip the SONAME
and SOVERSION fields (or the equivalent for non-ELF platforms).
Some deployments (e.g., Python wheels or Java `.jar` files) do not support
symlinks reliably and the way the libraries get loaded just leads to
unnecessary files in the packaged artifact.

@ -1,16 +0,0 @@
# Make field names from sources more descriptive
The VTK-m sources (like `Oscillator`, `Tangle`, and `Wavelet`) were all
creating fields with very generic names like `pointvar` or `scalars`. These
are very unhelpful names as it is impossible for downstream processes to
identify the meaning of these fields. Imagine having these data saved to a
file and then a different person trying to identify what they mean. Or
imagine dealing with more than one such source at a time and trying to
manage fields with similar or overlapping names.
The following renames happened:
* `Oscillator`: `scalars` -> `oscillating`
* `Tangle`: `pointvar` -> `tangle`
* `Wavelet`: `scalars` -> `RTData` (matches VTK source)

@ -1,11 +0,0 @@
# Enable `TypeToString` for `type_info`
VTK-m contains a helpful method named `vtkm::cont::TypeToString` that
either takes a type as a template argument or a `std::type_info` object and
returns a human-readable string for that type.
The standard C++ library has an alternate for `std::type_info` named
`std::type_index`, which has the added ability to be used in a container
like `set` or `map`. The `TypeToString` overloads have been extended to
also accept a `std::type_info` and report the name of the type stored in it
(rather than the name of `type_info` itself).

@ -1,24 +0,0 @@
# Added copy methods to `UnknownArrayHandle`
`vtkm::cont::UnknownArrayHandle` now provides a set of method that allows
you to copy data from one `UnknownArrayHandle` to another. The first
method, `DeepCopyFrom`, takes a source `UnknownArrayHandle` and deep copies
the data to the called one. If the `UnknownArrayHandle` already points to a
real `ArrayHandle`, the data is copied into that `ArrayHandle`. If the
`UnknownArrayHandle` does not point to an existing `ArrayHandle`, then a
new `ArrayHandleBasic` with the same value type as the source is created
and copied into.
The second method, `CopyShallowIfPossibleFrom` behaves similarly to
`DeepCopyFrom` except that it will perform a shallow copy if possible. That
is, if the target `UnknownArrayHandle` points to an `ArrayHandle` of the
same type as the source `UnknownArrayHandle`, then a shallow copy occurs
and the underlying `ArrayHandle` will point to the source. If the types
differ, then a deep copy is performed. If the target `UnknownArrayHandle`
does not point to an `ArrayHandle`, then the behavior is the same as the
`=` operator.
One of the intentions of these new methods is to allow you to copy arrays
without using a device compiler (e.g. `nvcc`). Calling `ArrayCopy` requires
you to include the `ArrayCopy.h` header file, and that in turn requires
device adapter algorithms. These methods insulate you from these.

@ -1,7 +0,0 @@
# Support writing binary files to legacy VTK files
The legacy VTK file writer writes out in ASCII. This is helpful when a
human is trying to read the file. However, if you have more than a
trivial amount of data, the file can get impractically large. To get
around this, `VTKDataSetWriter` now has a flag that allows you to write
the data in binary format.

@ -1 +1 @@
1.6.0
1.7.0-rc1

@ -14,7 +14,6 @@
#include <vtkm/TopologyElementTag.h>
#include <vtkm/cont/CellSet.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/exec/ConnectivityStructured.h>
#include <vtkm/internal/ConnectivityStructuredInternals.h>

@ -14,6 +14,8 @@
#include <vtkm/cont/Field.h>
#include <vtkm/cont/PartitionedDataSet.h>
#include <numeric>
namespace vtkm
{
namespace cont

@ -10,8 +10,6 @@
#define vtk_m_cont_StorageVirtual_cxx
#include <vtkm/cont/StorageVirtual.h>
#include <vtkm/cont/DeviceAdapter.h>
namespace vtkm
{
namespace cont

@ -10,6 +10,7 @@
#ifndef vtk_m_cont_StorageVirtual_hxx
#define vtk_m_cont_StorageVirtual_hxx
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/StorageVirtual.h>
#include <vtkm/cont/TryExecute.h>

@ -7,6 +7,8 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/DeviceAdapterList.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/Timer.h>

@ -10,9 +10,7 @@
#ifndef vtk_m_cont_Timer_h
#define vtk_m_cont_Timer_h
#include <vtkm/List.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/DeviceAdapterList.h>
#include <vtkm/cont/DeviceAdapterTag.h>
#include <vtkm/cont/vtkm_cont_export.h>

@ -33,27 +33,8 @@ namespace cont
namespace detail
{
#ifdef VTKM_GCC
// On rare occasion, we have seen errors like this:
// `_ZN4vtkm4cont6detailL27UnknownAHNumberOfComponentsIxEEiv' referenced in section
// `.data.rel.ro.local' of CMakeFiles/UnitTests_vtkm_cont_testing.dir/UnitTestCellSet.cxx.o:
// defined in discarded section
// `.text._ZN4vtkm4cont6detailL27UnknownAHNumberOfComponentsIxEEiv[_ZN4vtkm4cont14ArrayGetValuesINS0_15StorageTagBasicExNS0_18StorageTagCountingES2_EEvRKNS0_11ArrayHandleIxT_EERKNS4_IT0_T1_EERNS4_IS9_T2_EE]'
// of CMakeFiles/UnitTests_vtkm_cont_testing.dir/UnitTestCellSet.cxx.o
// I don't know what circumstances exactly lead up to this, but it appears that the compiler is
// being overly aggressive with removing unused symbols. In this instance, it seems to have removed
// a function actually being used. This might be a bug in the compiler (I happen to have seen it in
// gcc 8.3, 9.4, and 11.0), or it could be caused by a link-time optimizer. The problem should be
// able to be solved by explictly saying that this templated method is being used. (I can't think
// of any circumstances where this template would be instantiated but not used.) If the compiler
// knows this is being used, it should know all the templated methods internal are also used.
#define VTKM_FORCE_USED __attribute__((used))
#else
#define VTKM_FORCE_USED
#endif
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHDelete(void* mem)
void UnknownAHDelete(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -61,13 +42,13 @@ VTKM_FORCE_USED static void UnknownAHDelete(void* mem)
}
template <typename T, typename S>
VTKM_FORCE_USED static void* UnknownAHNewInstance()
void* UnknownAHNewInstance()
{
return new vtkm::cont::ArrayHandle<T, S>;
}
template <typename T, typename S>
VTKM_FORCE_USED static vtkm::Id UnknownAHNumberOfValues(void* mem)
vtkm::Id UnknownAHNumberOfValues(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -88,7 +69,7 @@ struct UnknownAHNumberOfComponentsImpl<T, vtkm::VecTraitsTagSizeVariable>
};
template <typename T>
VTKM_FORCE_USED static vtkm::IdComponent UnknownAHNumberOfComponents()
vtkm::IdComponent UnknownAHNumberOfComponents()
{
return UnknownAHNumberOfComponentsImpl<T>::Value;
}
@ -107,16 +88,16 @@ struct UnknownAHNumberOfComponentsFlatImpl<T, vtkm::VecTraitsTagSizeVariable>
};
template <typename T>
VTKM_FORCE_USED static vtkm::IdComponent UnknownAHNumberOfComponentsFlat()
vtkm::IdComponent UnknownAHNumberOfComponentsFlat()
{
return UnknownAHNumberOfComponentsFlatImpl<T>::Value;
}
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHAllocate(void* mem,
vtkm::Id numValues,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token)
void UnknownAHAllocate(void* mem,
vtkm::Id numValues,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -124,7 +105,7 @@ VTKM_FORCE_USED static void UnknownAHAllocate(void* mem,
}
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHShallowCopy(const void* sourceMem, void* destinationMem)
void UnknownAHShallowCopy(const void* sourceMem, void* destinationMem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
const AH* source = reinterpret_cast<const AH*>(sourceMem);
@ -133,7 +114,7 @@ VTKM_FORCE_USED static void UnknownAHShallowCopy(const void* sourceMem, void* de
}
template <typename T, typename S>
VTKM_FORCE_USED static std::vector<vtkm::cont::internal::Buffer>
std::vector<vtkm::cont::internal::Buffer>
UnknownAHExtractComponent(void* mem, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
@ -144,7 +125,7 @@ UnknownAHExtractComponent(void* mem, vtkm::IdComponent componentIndex, vtkm::Cop
}
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHReleaseResources(void* mem)
void UnknownAHReleaseResources(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -152,7 +133,7 @@ VTKM_FORCE_USED static void UnknownAHReleaseResources(void* mem)
}
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHReleaseResourcesExecution(void* mem)
void UnknownAHReleaseResourcesExecution(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -160,7 +141,7 @@ VTKM_FORCE_USED static void UnknownAHReleaseResourcesExecution(void* mem)
}
template <typename T, typename S>
VTKM_FORCE_USED static void UnknownAHPrintSummary(void* mem, std::ostream& out, bool full)
void UnknownAHPrintSummary(void* mem, std::ostream& out, bool full)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
@ -300,47 +281,42 @@ private:
};
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(
vtkm::VecTraitsTagSizeStatic)
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeStatic)
{
return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic<T>{});
}
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(
vtkm::VecTraitsTagSizeVariable)
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeVariable)
{
throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " +
vtkm::cont::TypeToString<T>());
}
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic()
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic()
{
return UnknownAHNewInstanceBasic<T>(typename vtkm::VecTraits<T>::IsSizeStatic{});
}
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(
vtkm::VecTraitsTagSizeStatic)
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(vtkm::VecTraitsTagSizeStatic)
{
using FloatT = typename vtkm::VecTraits<T>::template ReplaceBaseComponentType<vtkm::FloatDefault>;
return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic<FloatT>{});
}
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(
vtkm::VecTraitsTagSizeVariable)
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(vtkm::VecTraitsTagSizeVariable)
{
throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " +
vtkm::cont::TypeToString<T>());
}
template <typename T>
VTKM_FORCE_USED static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic()
std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic()
{
return UnknownAHNewInstanceFloatBasic<T>(typename vtkm::VecTraits<T>::IsSizeStatic{});
}
template <typename T, typename S>
VTKM_FORCE_USED inline UnknownAHContainer::UnknownAHContainer(
const vtkm::cont::ArrayHandle<T, S>& array)
inline UnknownAHContainer::UnknownAHContainer(const vtkm::cont::ArrayHandle<T, S>& array)
: ArrayHandlePointer(new vtkm::cont::ArrayHandle<T, S>(array))
, ValueType(typeid(T))
, StorageType(typeid(S))

@ -12,6 +12,7 @@
#include <vtkm/cont/CoordinateSystem.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/ErrorExecution.h>
#include <vtkm/cont/Field.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/Logging.h>

@ -256,11 +256,6 @@ inline VTKM_CONT vtkm::cont::DataSet Lagrangian::DoExecute(
vtkm::cont::ArrayCopy(BasisParticles, BasisParticlesOriginal);
}
if (!fieldMeta.IsPointField())
{
throw vtkm::cont::ErrorFilterExecution("Point field expected.");
}
if (this->writeFrequency == 0)
{
throw vtkm::cont::ErrorFilterExecution(
@ -284,7 +279,7 @@ inline VTKM_CONT vtkm::cont::DataSet Lagrangian::DoExecute(
vtkm::worklet::ParticleAdvection particleadvection;
vtkm::worklet::ParticleAdvectionResult<vtkm::Particle> res;
FieldType velocities(field);
FieldType velocities(field, fieldMeta.GetAssociation());
GridEvalType gridEval(coords, cells, velocities);
Stepper rk4(gridEval, static_cast<vtkm::Float32>(this->stepSize));

@ -75,11 +75,6 @@ inline VTKM_CONT vtkm::cont::DataSet LagrangianStructures::DoExecute(
const vtkm::filter::FieldMetadata& fieldMeta,
const vtkm::filter::PolicyBase<DerivedPolicy>&)
{
if (!fieldMeta.IsPointField())
{
throw vtkm::cont::ErrorFilterExecution("Point field expected.");
}
using Structured2DType = vtkm::cont::CellSetStructured<2>;
using Structured3DType = vtkm::cont::CellSetStructured<3>;
@ -137,7 +132,7 @@ inline VTKM_CONT vtkm::cont::DataSet LagrangianStructures::DoExecute(
{
vtkm::cont::Invoker invoke;
FieldType velocities(field);
FieldType velocities(field, fieldMeta.GetAssociation());
GridEvaluator evaluator(input.GetCoordinateSystem(), input.GetCellSet(), velocities);
Stepper integrator(evaluator, stepSize);
vtkm::worklet::ParticleAdvection particles;

@ -51,9 +51,6 @@ inline VTKM_CONT vtkm::cont::DataSet StreamSurface::DoExecute(
const vtkm::cont::CoordinateSystem& coords =
input.GetCoordinateSystem(this->GetActiveCoordinateSystemIndex());
if (!fieldMeta.IsPointField())
throw vtkm::cont::ErrorFilterExecution("Point field expected.");
using FieldHandle = vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>, StorageType>;
using FieldType = vtkm::worklet::particleadvection::VelocityField<FieldHandle>;
using GridEvalType = vtkm::worklet::particleadvection::GridEvaluator<FieldType>;
@ -61,7 +58,7 @@ inline VTKM_CONT vtkm::cont::DataSet StreamSurface::DoExecute(
using Stepper = vtkm::worklet::particleadvection::Stepper<RK4Type, GridEvalType>;
//compute streamlines
FieldType velocities(field);
FieldType velocities(field, fieldMeta.GetAssociation());
GridEvalType eval(coords, cells, velocities);
Stepper rk4(eval, this->StepSize);

@ -93,12 +93,13 @@ public:
: DataSetIntegratorBase<vtkm::worklet::particleadvection::GridEvaluator<
vtkm::worklet::particleadvection::VelocityField<FieldHandleType>>>(false, id)
{
using Association = vtkm::cont::Field::Association;
using FieldType = vtkm::worklet::particleadvection::VelocityField<FieldHandleType>;
using EvalType = vtkm::worklet::particleadvection::GridEvaluator<FieldType>;
Association association = ds.GetField(fieldNm).GetAssociation();
auto fieldArray = this->GetFieldHandle(ds, fieldNm);
using EvalType = vtkm::worklet::particleadvection::GridEvaluator<
vtkm::worklet::particleadvection::VelocityField<FieldHandleType>>;
this->Eval = std::shared_ptr<EvalType>(new EvalType(ds, fieldArray));
FieldType field(fieldArray, association);
this->Eval = std::shared_ptr<EvalType>(new EvalType(ds, field));
}
};

@ -37,17 +37,10 @@ set(template_sources
)
set(sources
FileUtils.cxx
BOVDataSetReader.cxx
DecodePNG.cxx
EncodePNG.cxx
)
# TODO: None of these codes actually use a device. Rather, they access ArrayHandle, and we
# currently need to ensure that ArrayHandle is correctly compiled for all devices. This is
# kind of silly, so hopefully sometime in the future you will no longer need to compile for
# devices for ArrayHandle, and this requirement will go away.
set(device_sources
BOVDataSetReader.cxx
FileUtils.cxx
ImageReaderBase.cxx
ImageReaderPNG.cxx
ImageReaderPNM.cxx
@ -56,22 +49,26 @@ set(device_sources
ImageWriterPNG.cxx
ImageWriterPNM.cxx
VTKDataSetReader.cxx
VTKDataSetReaderBase.cxx
VTKDataSetWriter.cxx
VTKPolyDataReader.cxx
VTKRectilinearGridReader.cxx
VTKStructuredGridReader.cxx
VTKStructuredPointsReader.cxx
VTKUnstructuredGridReader.cxx
)
# These files use ArrayCopy which uses a WorkletMapField thus are required to be device_sources
set(device_sources
VTKDataSetReaderBase.cxx
VTKRectilinearGridReader.cxx
)
if (VTKm_ENABLE_HDF5_IO)
set(headers
${headers}
ImageReaderHDF5.h
ImageWriterHDF5.h)
set(device_sources
${device_sources}
set(sources
${sources}
ImageReaderHDF5.cxx
ImageWriterHDF5.cxx)
endif ()

@ -7,12 +7,14 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/io/ImageUtils.h>
#include <vtkm/cont/ErrorExecution.h>
#include <vtkm/io/FileUtils.h>
#include <vtkm/io/ImageReaderBase.h>
#include <vtkm/io/ImageReaderPNG.h>
#include <vtkm/io/ImageReaderPNM.h>
#include <vtkm/io/ImageUtils.h>
#include <vtkm/io/ImageWriterBase.h>
#include <vtkm/io/ImageWriterPNG.h>
#include <vtkm/io/ImageWriterPNM.h>

@ -18,7 +18,6 @@
#include <unistd.h>
#endif
#include <random>
#include <string>
using namespace vtkm::io;

@ -13,7 +13,6 @@
#include <vtkm/io/ImageReaderPNM.h>
#include <vtkm/io/ImageWriterPNG.h>
#include <vtkm/io/ImageWriterPNM.h>
#include <vtkm/io/PixelTypes.h>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/Color.h>

@ -36,6 +36,7 @@ set(headers
DecodePNG.h # deprecated
EncodePNG.h # deprecated
LineRenderer.h
LineRendererBatcher.h
MatrixHelpers.h
Scene.h
Mapper.h
@ -52,6 +53,7 @@ set(headers
TextAnnotationBillboard.h
TextAnnotationScreen.h
TextRenderer.h
TextRendererBatcher.h
Texture2D.h
Triangulator.h
View.h
@ -85,6 +87,7 @@ set(device_sources
ColorLegendAnnotation.cxx
ConnectivityProxy.cxx
LineRenderer.cxx
LineRendererBatcher.cxx
Mapper.cxx
MapperConnectivity.cxx
MapperCylinder.cxx
@ -99,6 +102,7 @@ set(device_sources
TextAnnotationBillboard.cxx
TextAnnotationScreen.cxx
TextRenderer.cxx
TextRendererBatcher.cxx
View.cxx
View1D.cxx
View2D.cxx

@ -20,6 +20,7 @@
#include <vtkm/rendering/BitmapFontFactory.h>
#include <vtkm/rendering/LineRenderer.h>
#include <vtkm/rendering/TextRenderer.h>
#include <vtkm/rendering/TextRendererBatcher.h>
#include <vtkm/rendering/WorldAnnotator.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
@ -224,6 +225,7 @@ struct Canvas::CanvasInternals
FontTextureType FontTexture;
vtkm::Matrix<vtkm::Float32, 4, 4> ModelView;
vtkm::Matrix<vtkm::Float32, 4, 4> Projection;
std::shared_ptr<vtkm::rendering::TextRendererBatcher> TextBatcher;
};
Canvas::Canvas(vtkm::Id width, vtkm::Id height)
@ -394,8 +396,11 @@ void Canvas::AddLine(const vtkm::Vec2f_64& point0,
const vtkm::rendering::Color& color) const
{
vtkm::rendering::Canvas* self = const_cast<vtkm::rendering::Canvas*>(this);
LineRenderer renderer(self, vtkm::MatrixMultiply(Internals->Projection, Internals->ModelView));
vtkm::rendering::LineRendererBatcher lineBatcher;
LineRenderer renderer(
self, vtkm::MatrixMultiply(Internals->Projection, Internals->ModelView), &lineBatcher);
renderer.RenderLine(point0, point1, linewidth, color);
lineBatcher.Render(self);
}
void Canvas::AddLine(vtkm::Float64 x0,
@ -476,16 +481,14 @@ void Canvas::AddText(const vtkm::Matrix<vtkm::Float32, 4, 4>& transform,
const std::string& text,
const vtkm::Float32& depth) const
{
if (!Internals->FontTexture.IsValid())
if (!this->EnsureFontLoaded() || !this->Internals->TextBatcher)
{
if (!LoadFont())
{
return;
}
return;
}
vtkm::rendering::Canvas* self = const_cast<vtkm::rendering::Canvas*>(this);
TextRenderer fontRenderer(self, Internals->Font, Internals->FontTexture);
vtkm::rendering::TextRenderer fontRenderer(
self, Internals->Font, Internals->FontTexture, this->Internals->TextBatcher.get());
fontRenderer.RenderText(transform, scale, anchor, color, text, depth);
}
@ -527,6 +530,40 @@ void Canvas::AddText(vtkm::Float32 x,
text);
}
void Canvas::BeginTextRenderingBatch() const
{
if (!this->EnsureFontLoaded() || this->Internals->TextBatcher)
{
return;
}
this->Internals->TextBatcher =
std::make_shared<vtkm::rendering::TextRendererBatcher>(this->Internals->FontTexture);
}
void Canvas::EndTextRenderingBatch() const
{
if (!this->Internals->TextBatcher)
{
return;
}
this->Internals->TextBatcher->Render(this);
this->Internals->TextBatcher.reset();
}
bool Canvas::EnsureFontLoaded() const
{
if (!Internals->FontTexture.IsValid())
{
if (!LoadFont())
{
return false;
}
}
return true;
}
bool Canvas::LoadFont() const
{
Internals->Font = BitmapFontFactory::CreateLiberation2Sans();

@ -204,6 +204,11 @@ public:
const std::string& text,
const vtkm::Float32& depth = 0) const;
VTKM_CONT
void BeginTextRenderingBatch() const;
VTKM_CONT
void EndTextRenderingBatch() const;
friend class AxisAnnotation2D;
friend class ColorBarAnnotation;
@ -215,6 +220,8 @@ public:
private:
bool LoadFont() const;
bool EnsureFontLoaded() const;
const vtkm::Matrix<vtkm::Float32, 4, 4>& GetModelView() const;
const vtkm::Matrix<vtkm::Float32, 4, 4>& GetProjection() const;

@ -12,6 +12,7 @@
#include <vtkm/Transform3D.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/rendering/LineRendererBatcher.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
@ -21,9 +22,11 @@ namespace rendering
{
LineRenderer::LineRenderer(const vtkm::rendering::Canvas* canvas,
vtkm::Matrix<vtkm::Float32, 4, 4> transform)
vtkm::Matrix<vtkm::Float32, 4, 4> transform,
vtkm::rendering::LineRendererBatcher* lineBatcher)
: Canvas(canvas)
, Transform(transform)
, LineBatcher(lineBatcher)
{
}
@ -45,85 +48,7 @@ void LineRenderer::RenderLine(const vtkm::Vec3f_64& point0,
{
vtkm::Vec3f_32 p0 = TransformPoint(point0);
vtkm::Vec3f_32 p1 = TransformPoint(point1);
vtkm::Id x0 = static_cast<vtkm::Id>(vtkm::Round(p0[0]));
vtkm::Id y0 = static_cast<vtkm::Id>(vtkm::Round(p0[1]));
vtkm::Float32 z0 = static_cast<vtkm::Float32>(p0[2]);
vtkm::Id x1 = static_cast<vtkm::Id>(vtkm::Round(p1[0]));
vtkm::Id y1 = static_cast<vtkm::Id>(vtkm::Round(p1[1]));
vtkm::Float32 z1 = static_cast<vtkm::Float32>(p1[2]);
vtkm::Id dx = vtkm::Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
vtkm::Id dy = -vtkm::Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
vtkm::Id err = dx + dy, err2 = 0;
const vtkm::Id xStart = x0;
const vtkm::Id yStart = y0;
const vtkm::Float32 pdist = vtkm::Sqrt(vtkm::Float32(dx * dx) + vtkm::Float32(dy * dy));
auto colorPortal =
vtkm::rendering::Canvas::ColorBufferType(Canvas->GetColorBuffer()).WritePortal();
auto depthPortal =
vtkm::rendering::Canvas::DepthBufferType(Canvas->GetDepthBuffer()).WritePortal();
vtkm::Vec4f_32 colorC = color.Components;
while (x0 >= 0 && x0 < Canvas->GetWidth() && y0 >= 0 && y0 < Canvas->GetHeight())
{
vtkm::Float32 deltaX = static_cast<vtkm::Float32>(x0 - xStart);
vtkm::Float32 deltaY = static_cast<vtkm::Float32>(y0 - yStart);
// Depth is wrong, but its far less wrong that it used to be.
// These depth values are in screen space, which have been
// potentially tranformed by a perspective correction.
// To interpolated the depth correctly, there must be a perspective correction.
// I haven't looked, but the wireframmer probably suffers from this too.
// Additionally, this should not happen on the CPU. Annotations take
// far longer than the the geometry.
vtkm::Float32 t = pdist == 0.f ? 1.0f : vtkm::Sqrt(deltaX * deltaX + deltaY * deltaY) / pdist;
t = vtkm::Min(1.f, vtkm::Max(0.f, t));
vtkm::Float32 z = vtkm::Lerp(z0, z1, t);
vtkm::Id index = y0 * Canvas->GetWidth() + x0;
vtkm::Vec4f_32 currentColor = colorPortal.Get(index);
vtkm::Float32 currentZ = depthPortal.Get(index);
bool blend = currentColor[3] < 1.f && z > currentZ;
if (currentZ > z || blend)
{
vtkm::Vec4f_32 writeColor = colorC;
vtkm::Float32 depth = z;
if (blend)
{
// If there is any transparency, all alphas
// have been pre-mulitplied
vtkm::Float32 alpha = (1.f - currentColor[3]);
writeColor[0] = currentColor[0] + colorC[0] * alpha;
writeColor[1] = currentColor[1] + colorC[1] * alpha;
writeColor[2] = currentColor[2] + colorC[2] * alpha;
writeColor[3] = 1.f * alpha + currentColor[3]; // we are always drawing opaque lines
// keep the current z. Line z interpolation is not accurate
// Matt: this is correct. Interpolation is wrong
depth = currentZ;
}
depthPortal.Set(index, depth);
colorPortal.Set(index, writeColor);
}
if (x0 == x1 && y0 == y1)
{
break;
}
err2 = err * 2;
if (err2 >= dy)
{
err += dy;
x0 += sx;
}
if (err2 <= dx)
{
err += dx;
y0 += sy;
}
}
this->LineBatcher->BatchLine(p0, p1, color);
}
vtkm::Vec3f_32 LineRenderer::TransformPoint(const vtkm::Vec3f_64& point) const

@ -20,12 +20,15 @@ namespace vtkm
{
namespace rendering
{
class LineRendererBatcher;
class VTKM_RENDERING_EXPORT LineRenderer
{
public:
VTKM_CONT
LineRenderer(const vtkm::rendering::Canvas* canvas, vtkm::Matrix<vtkm::Float32, 4, 4> transform);
LineRenderer(const vtkm::rendering::Canvas* canvas,
vtkm::Matrix<vtkm::Float32, 4, 4> transform,
vtkm::rendering::LineRendererBatcher* lineBatcher);
VTKM_CONT
void RenderLine(const vtkm::Vec2f_64& point0,
@ -45,6 +48,7 @@ private:
const vtkm::rendering::Canvas* Canvas;
vtkm::Matrix<vtkm::Float32, 4, 4> Transform;
vtkm::rendering::LineRendererBatcher* LineBatcher;
}; // class LineRenderer
}
} // namespace vtkm::rendering

@ -0,0 +1,168 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/rendering/LineRendererBatcher.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace rendering
{
namespace
{
using ColorsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Vec4f_32>;
using PointsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
struct RenderLine : public vtkm::worklet::WorkletMapField
{
using ColorBufferType = vtkm::rendering::Canvas::ColorBufferType;
using DepthBufferType = vtkm::rendering::Canvas::DepthBufferType;
using ControlSignature = void(FieldIn, FieldIn, FieldIn, WholeArrayInOut, WholeArrayInOut);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
VTKM_CONT
RenderLine() {}
VTKM_CONT
RenderLine(vtkm::Id width, vtkm::Id height)
: Width(width)
, Height(height)
{
}
template <typename ColorBufferPortal, typename DepthBufferPortal>
VTKM_EXEC void operator()(const vtkm::Vec3f_32& start,
const vtkm::Vec3f_32& end,
const vtkm::Vec4f_32& color,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer) const
{
vtkm::Id x0 = static_cast<vtkm::Id>(vtkm::Round(start[0]));
vtkm::Id y0 = static_cast<vtkm::Id>(vtkm::Round(start[1]));
vtkm::Float32 z0 = static_cast<vtkm::Float32>(start[2]);
vtkm::Id x1 = static_cast<vtkm::Id>(vtkm::Round(end[0]));
vtkm::Id y1 = static_cast<vtkm::Id>(vtkm::Round(end[1]));
vtkm::Float32 z1 = static_cast<vtkm::Float32>(end[2]);
vtkm::Id dx = vtkm::Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
vtkm::Id dy = -vtkm::Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
vtkm::Id err = dx + dy, err2 = 0;
const vtkm::Id xStart = x0;
const vtkm::Id yStart = y0;
const vtkm::Float32 pdist = vtkm::Sqrt(vtkm::Float32(dx * dx) + vtkm::Float32(dy * dy));
while (x0 >= 0 && x0 < this->Width && y0 >= 0 && y0 < this->Height)
{
vtkm::Float32 deltaX = static_cast<vtkm::Float32>(x0 - xStart);
vtkm::Float32 deltaY = static_cast<vtkm::Float32>(y0 - yStart);
// Depth is wrong, but its far less wrong that it used to be.
// These depth values are in screen space, which have been
// potentially tranformed by a perspective correction.
// To interpolated the depth correctly, there must be a perspective correction.
// I haven't looked, but the wireframmer probably suffers from this too.
// Additionally, this should not happen on the CPU. Annotations take
// far longer than the the geometry.
vtkm::Float32 t = pdist == 0.f ? 1.0f : vtkm::Sqrt(deltaX * deltaX + deltaY * deltaY) / pdist;
t = vtkm::Min(1.f, vtkm::Max(0.f, t));
vtkm::Float32 z = vtkm::Lerp(z0, z1, t);
vtkm::Id index = y0 * this->Width + x0;
vtkm::Vec4f_32 currentColor = colorBuffer.Get(index);
vtkm::Float32 currentZ = depthBuffer.Get(index);
bool blend = currentColor[3] < 1.f && z > currentZ;
if (currentZ > z || blend)
{
vtkm::Vec4f_32 writeColor = color;
vtkm::Float32 depth = z;
if (blend)
{
// If there is any transparency, all alphas
// have been pre-mulitplied
vtkm::Float32 alpha = (1.f - currentColor[3]);
writeColor[0] = currentColor[0] + color[0] * alpha;
writeColor[1] = currentColor[1] + color[1] * alpha;
writeColor[2] = currentColor[2] + color[2] * alpha;
writeColor[3] = 1.f * alpha + currentColor[3]; // we are always drawing opaque lines
// keep the current z. Line z interpolation is not accurate
// Matt: this is correct. Interpolation is wrong
depth = currentZ;
}
depthBuffer.Set(index, depth);
colorBuffer.Set(index, writeColor);
}
if (x0 == x1 && y0 == y1)
{
break;
}
err2 = err * 2;
if (err2 >= dy)
{
err += dy;
x0 += sx;
}
if (err2 <= dx)
{
err += dx;
y0 += sy;
}
}
}
vtkm::Id Width;
vtkm::Id Height;
}; // struct RenderLine
} // namespace
LineRendererBatcher::LineRendererBatcher() {}
void LineRendererBatcher::BatchLine(const vtkm::Vec3f_64& start,
const vtkm::Vec3f_64& end,
const vtkm::rendering::Color& color)
{
vtkm::Vec3f_32 start32(static_cast<vtkm::Float32>(start[0]),
static_cast<vtkm::Float32>(start[1]),
static_cast<vtkm::Float32>(start[2]));
vtkm::Vec3f_32 end32(static_cast<vtkm::Float32>(end[0]),
static_cast<vtkm::Float32>(end[1]),
static_cast<vtkm::Float32>(end[2]));
this->BatchLine(start32, end32, color);
}
void LineRendererBatcher::BatchLine(const vtkm::Vec3f_32& start,
const vtkm::Vec3f_32& end,
const vtkm::rendering::Color& color)
{
this->Starts.push_back(start);
this->Ends.push_back(end);
this->Colors.push_back(color.Components);
}
void LineRendererBatcher::Render(const vtkm::rendering::Canvas* canvas) const
{
PointsArrayHandle starts = vtkm::cont::make_ArrayHandle(this->Starts, vtkm::CopyFlag::Off);
PointsArrayHandle ends = vtkm::cont::make_ArrayHandle(this->Ends, vtkm::CopyFlag::Off);
ColorsArrayHandle colors = vtkm::cont::make_ArrayHandle(this->Colors, vtkm::CopyFlag::Off);
vtkm::cont::Invoker invoker;
invoker(RenderLine(canvas->GetWidth(), canvas->GetHeight()),
starts,
ends,
colors,
canvas->GetColorBuffer(),
canvas->GetDepthBuffer());
}
}
} // namespace vtkm::rendering

@ -0,0 +1,52 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_rendering_LineRendererBatcher_h
#define vtk_m_rendering_LineRendererBatcher_h
#include <string>
#include <vector>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/Color.h>
#include <vtkm/rendering/vtkm_rendering_export.h>
namespace vtkm
{
namespace rendering
{
class VTKM_RENDERING_EXPORT LineRendererBatcher
{
public:
VTKM_CONT
LineRendererBatcher();
VTKM_CONT
void BatchLine(const vtkm::Vec3f_64& start,
const vtkm::Vec3f_64& end,
const vtkm::rendering::Color& color);
VTKM_CONT
void BatchLine(const vtkm::Vec3f_32& start,
const vtkm::Vec3f_32& end,
const vtkm::rendering::Color& color);
void Render(const vtkm::rendering::Canvas* canvas) const;
private:
std::vector<vtkm::Vec3f_32> Starts;
std::vector<vtkm::Vec3f_32> Ends;
std::vector<vtkm::Vec4f_32> Colors;
};
}
} // namespace vtkm::rendering
#endif // vtk_m_rendering_LineRendererBatcher_h

@ -12,6 +12,7 @@
#include <vtkm/Transform3D.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/rendering/TextRendererBatcher.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
@ -19,163 +20,15 @@ namespace vtkm
{
namespace rendering
{
namespace internal
{
struct RenderBitmapFont : public vtkm::worklet::WorkletMapField
{
using ColorBufferType = vtkm::rendering::Canvas::ColorBufferType;
using DepthBufferType = vtkm::rendering::Canvas::DepthBufferType;
using FontTextureType = vtkm::rendering::Canvas::FontTextureType;
using ControlSignature = void(FieldIn, FieldIn, ExecObject, WholeArrayInOut, WholeArrayInOut);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using InputDomain = _1;
VTKM_CONT
RenderBitmapFont() {}
VTKM_CONT
RenderBitmapFont(const vtkm::Vec4f_32& color,
vtkm::Id width,
vtkm::Id height,
vtkm::Float32 depth)
: Color(color)
, Width(width)
, Height(height)
, Depth(depth)
{
}
template <typename ColorBufferPortal, typename FontTexture, typename DepthBufferPortal>
VTKM_EXEC void operator()(const vtkm::Vec4f_32& screenCoords,
const vtkm::Vec4f_32& textureCoords,
const FontTexture& fontTexture,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer) const
{
vtkm::Float32 x0 = Clamp(screenCoords[0], 0.0f, static_cast<vtkm::Float32>(Width - 1));
vtkm::Float32 x1 = Clamp(screenCoords[2], 0.0f, static_cast<vtkm::Float32>(Width - 1));
vtkm::Float32 y0 = Clamp(screenCoords[1], 0.0f, static_cast<vtkm::Float32>(Height - 1));
vtkm::Float32 y1 = Clamp(screenCoords[3], 0.0f, static_cast<vtkm::Float32>(Height - 1));
// For crisp text rendering, we sample the font texture at points smaller than the pixel
// sizes. Here we sample at increments of 0.25f, and scale the reported intensities accordingly
vtkm::Float32 dx = x1 - x0, dy = y1 - y0;
for (vtkm::Float32 x = x0; x <= x1; x += 0.25f)
{
for (vtkm::Float32 y = y0; y <= y1; y += 0.25f)
{
vtkm::Float32 tu = x1 == x0 ? 1.0f : (x - x0) / dx;
vtkm::Float32 tv = y1 == y0 ? 1.0f : (y - y0) / dy;
vtkm::Float32 u = vtkm::Lerp(textureCoords[0], textureCoords[2], tu);
vtkm::Float32 v = vtkm::Lerp(textureCoords[1], textureCoords[3], tv);
vtkm::Float32 intensity = fontTexture.GetColor(u, v)[0] * 0.25f;
Plot(x, y, intensity, colorBuffer, depthBuffer);
}
}
}
template <typename ColorBufferPortal, typename DepthBufferPortal>
VTKM_EXEC void Plot(vtkm::Float32 x,
vtkm::Float32 y,
vtkm::Float32 intensity,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer) const
{
vtkm::Id index =
static_cast<vtkm::Id>(vtkm::Round(y)) * Width + static_cast<vtkm::Id>(vtkm::Round(x));
vtkm::Vec4f_32 srcColor = colorBuffer.Get(index);
vtkm::Float32 currentDepth = depthBuffer.Get(index);
bool swap = Depth > currentDepth;
intensity = intensity * Color[3];
vtkm::Vec4f_32 color = intensity * Color;
color[3] = intensity;
vtkm::Vec4f_32 front = color;
vtkm::Vec4f_32 back = srcColor;
if (swap)
{
front = srcColor;
back = color;
}
vtkm::Vec4f_32 blendedColor;
vtkm::Float32 alpha = (1.f - front[3]);
blendedColor[0] = front[0] + back[0] * alpha;
blendedColor[1] = front[1] + back[1] * alpha;
blendedColor[2] = front[2] + back[2] * alpha;
blendedColor[3] = back[3] * alpha + front[3];
colorBuffer.Set(index, blendedColor);
}
VTKM_EXEC
vtkm::Float32 Clamp(vtkm::Float32 v, vtkm::Float32 min, vtkm::Float32 max) const
{
return vtkm::Min(vtkm::Max(v, min), max);
}
vtkm::Vec4f_32 Color;
vtkm::Id Width;
vtkm::Id Height;
vtkm::Float32 Depth;
}; // struct RenderBitmapFont
struct RenderBitmapFontExecutor
{
using ColorBufferType = vtkm::rendering::Canvas::ColorBufferType;
using DepthBufferType = vtkm::rendering::Canvas::DepthBufferType;
using FontTextureType = vtkm::rendering::Canvas::FontTextureType;
using ScreenCoordsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Id4>;
using TextureCoordsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Vec4f_32>;
VTKM_CONT
RenderBitmapFontExecutor(const ScreenCoordsArrayHandle& screenCoords,
const TextureCoordsArrayHandle& textureCoords,
const FontTextureType& fontTexture,
const vtkm::Vec4f_32& color,
const ColorBufferType& colorBuffer,
const DepthBufferType& depthBuffer,
vtkm::Id width,
vtkm::Id height,
vtkm::Float32 depth)
: ScreenCoords(screenCoords)
, TextureCoords(textureCoords)
, FontTexture(fontTexture)
, ColorBuffer(colorBuffer)
, DepthBuffer(depthBuffer)
, Worklet(color, width, height, depth)
{
}
template <typename Device>
VTKM_CONT bool operator()(Device) const
{
VTKM_IS_DEVICE_ADAPTER_TAG(Device);
vtkm::worklet::DispatcherMapField<RenderBitmapFont> dispatcher(Worklet);
dispatcher.SetDevice(Device());
dispatcher.Invoke(
ScreenCoords, TextureCoords, FontTexture.GetExecObjectFactory(), ColorBuffer, DepthBuffer);
return true;
}
ScreenCoordsArrayHandle ScreenCoords;
TextureCoordsArrayHandle TextureCoords;
FontTextureType FontTexture;
ColorBufferType ColorBuffer;
DepthBufferType DepthBuffer;
RenderBitmapFont Worklet;
}; // struct RenderBitmapFontExecutor
} // namespace internal
TextRenderer::TextRenderer(const vtkm::rendering::Canvas* canvas,
const vtkm::rendering::BitmapFont& font,
const vtkm::rendering::Canvas::FontTextureType& fontTexture)
const vtkm::rendering::Canvas::FontTextureType& fontTexture,
vtkm::rendering::TextRendererBatcher* textBatcher)
: Canvas(canvas)
, Font(font)
, FontTexture(fontTexture)
, TextBatcher(textBatcher)
{
}
@ -226,8 +79,8 @@ void TextRenderer::RenderText(const vtkm::Matrix<vtkm::Float32, 4, 4>& transform
vtkm::Float32 fy = -(0.5f + 0.5f * anchor[1]);
vtkm::Float32 fz = 0;
using ScreenCoordsArrayHandle = internal::RenderBitmapFontExecutor::ScreenCoordsArrayHandle;
using TextureCoordsArrayHandle = internal::RenderBitmapFontExecutor::TextureCoordsArrayHandle;
using ScreenCoordsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Id4>;
using TextureCoordsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Vec4f_32>;
ScreenCoordsArrayHandle screenCoords;
TextureCoordsArrayHandle textureCoords;
{
@ -261,15 +114,7 @@ void TextRenderer::RenderText(const vtkm::Matrix<vtkm::Float32, 4, 4>& transform
}
}
vtkm::cont::TryExecute(internal::RenderBitmapFontExecutor(screenCoords,
textureCoords,
FontTexture,
color.Components,
Canvas->GetColorBuffer(),
Canvas->GetDepthBuffer(),
Canvas->GetWidth(),
Canvas->GetHeight(),
depth));
this->TextBatcher->BatchText(screenCoords, textureCoords, color, depth);
}
}
} // namespace vtkm::rendering

@ -22,6 +22,7 @@ namespace vtkm
{
namespace rendering
{
class TextRendererBatcher;
class VTKM_RENDERING_EXPORT TextRenderer
{
@ -29,7 +30,8 @@ public:
VTKM_CONT
TextRenderer(const vtkm::rendering::Canvas* canvas,
const vtkm::rendering::BitmapFont& font,
const vtkm::rendering::Canvas::FontTextureType& fontTexture);
const vtkm::rendering::Canvas::FontTextureType& fontTexture,
vtkm::rendering::TextRendererBatcher* textBatcher);
VTKM_CONT
void RenderText(const vtkm::Vec2f_32& position,
@ -61,6 +63,7 @@ private:
const vtkm::rendering::Canvas* Canvas;
vtkm::rendering::BitmapFont Font;
vtkm::rendering::Canvas::FontTextureType FontTexture;
vtkm::rendering::TextRendererBatcher* TextBatcher;
};
}
} // namespace vtkm::rendering

@ -0,0 +1,166 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/rendering/TextRendererBatcher.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace rendering
{
namespace
{
struct RenderBitmapFont : public vtkm::worklet::WorkletMapField
{
using ColorBufferType = vtkm::rendering::Canvas::ColorBufferType;
using DepthBufferType = vtkm::rendering::Canvas::DepthBufferType;
using FontTextureType = vtkm::rendering::Canvas::FontTextureType;
using ControlSignature =
void(FieldIn, FieldIn, FieldIn, FieldIn, ExecObject, WholeArrayInOut, WholeArrayInOut);
using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7);
using InputDomain = _1;
VTKM_CONT
RenderBitmapFont() {}
VTKM_CONT
RenderBitmapFont(vtkm::Id width, vtkm::Id height)
: Width(width)
, Height(height)
{
}
template <typename ColorBufferPortal, typename FontTexture, typename DepthBufferPortal>
VTKM_EXEC void operator()(const vtkm::Vec4f_32& screenCoords,
const vtkm::Vec4f_32& textureCoords,
const vtkm::Vec4f_32& color,
const vtkm::Float32& depth,
const FontTexture& fontTexture,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer) const
{
vtkm::Float32 x0 = Clamp(screenCoords[0], 0.0f, static_cast<vtkm::Float32>(Width - 1));
vtkm::Float32 x1 = Clamp(screenCoords[2], 0.0f, static_cast<vtkm::Float32>(Width - 1));
vtkm::Float32 y0 = Clamp(screenCoords[1], 0.0f, static_cast<vtkm::Float32>(Height - 1));
vtkm::Float32 y1 = Clamp(screenCoords[3], 0.0f, static_cast<vtkm::Float32>(Height - 1));
// For crisp text rendering, we sample the font texture at points smaller than the pixel
// sizes. Here we sample at increments of 0.25f, and scale the reported intensities accordingly
vtkm::Float32 dx = x1 - x0, dy = y1 - y0;
for (vtkm::Float32 x = x0; x <= x1; x += 0.25f)
{
for (vtkm::Float32 y = y0; y <= y1; y += 0.25f)
{
vtkm::Float32 tu = x1 == x0 ? 1.0f : (x - x0) / dx;
vtkm::Float32 tv = y1 == y0 ? 1.0f : (y - y0) / dy;
vtkm::Float32 u = vtkm::Lerp(textureCoords[0], textureCoords[2], tu);
vtkm::Float32 v = vtkm::Lerp(textureCoords[1], textureCoords[3], tv);
vtkm::Float32 intensity = fontTexture.GetColor(u, v)[0] * 0.25f;
Plot(x, y, intensity, color, depth, colorBuffer, depthBuffer);
}
}
}
template <typename ColorBufferPortal, typename DepthBufferPortal>
VTKM_EXEC void Plot(vtkm::Float32 x,
vtkm::Float32 y,
vtkm::Float32 intensity,
vtkm::Vec4f_32 color,
vtkm::Float32 depth,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer) const
{
vtkm::Id index =
static_cast<vtkm::Id>(vtkm::Round(y)) * Width + static_cast<vtkm::Id>(vtkm::Round(x));
vtkm::Vec4f_32 srcColor = colorBuffer.Get(index);
vtkm::Float32 currentDepth = depthBuffer.Get(index);
bool swap = depth > currentDepth;
intensity = intensity * color[3];
color = intensity * color;
color[3] = intensity;
vtkm::Vec4f_32 front = color;
vtkm::Vec4f_32 back = srcColor;
if (swap)
{
front = srcColor;
back = color;
}
vtkm::Vec4f_32 blendedColor;
vtkm::Float32 alpha = (1.f - front[3]);
blendedColor[0] = front[0] + back[0] * alpha;
blendedColor[1] = front[1] + back[1] * alpha;
blendedColor[2] = front[2] + back[2] * alpha;
blendedColor[3] = back[3] * alpha + front[3];
colorBuffer.Set(index, blendedColor);
}
VTKM_EXEC
vtkm::Float32 Clamp(vtkm::Float32 v, vtkm::Float32 min, vtkm::Float32 max) const
{
return vtkm::Min(vtkm::Max(v, min), max);
}
vtkm::Id Width;
vtkm::Id Height;
}; // struct RenderBitmapFont
} // namespace
TextRendererBatcher::TextRendererBatcher(
const vtkm::rendering::Canvas::FontTextureType& fontTexture)
: FontTexture(fontTexture)
{
}
void TextRendererBatcher::BatchText(const ScreenCoordsArrayHandle& screenCoords,
const TextureCoordsArrayHandle& textureCoords,
const vtkm::rendering::Color& color,
const vtkm::Float32& depth)
{
vtkm::Id textLength = screenCoords.GetNumberOfValues();
ScreenCoordsArrayHandle::ReadPortalType screenCoordsP = screenCoords.ReadPortal();
TextureCoordsArrayHandle::ReadPortalType textureCoordsP = textureCoords.ReadPortal();
for (int i = 0; i < textLength; ++i)
{
this->ScreenCoords.push_back(screenCoordsP.Get(i));
this->TextureCoords.push_back(textureCoordsP.Get(i));
this->Colors.push_back(color.Components);
this->Depths.push_back(depth);
}
}
void TextRendererBatcher::Render(const vtkm::rendering::Canvas* canvas) const
{
ScreenCoordsArrayHandle screenCoords =
vtkm::cont::make_ArrayHandle(this->ScreenCoords, vtkm::CopyFlag::Off);
TextureCoordsArrayHandle textureCoords =
vtkm::cont::make_ArrayHandle(this->TextureCoords, vtkm::CopyFlag::Off);
vtkm::cont::ArrayHandle<ColorType> colors =
vtkm::cont::make_ArrayHandle(this->Colors, vtkm::CopyFlag::Off);
vtkm::cont::ArrayHandle<vtkm::Float32> depths =
vtkm::cont::make_ArrayHandle(this->Depths, vtkm::CopyFlag::Off);
vtkm::cont::Invoker invoker;
invoker(RenderBitmapFont(canvas->GetWidth(), canvas->GetHeight()),
screenCoords,
textureCoords,
colors,
depths,
this->FontTexture.GetExecObjectFactory(),
canvas->GetColorBuffer(),
canvas->GetDepthBuffer());
}
}
} // namespace vtkm::rendering

@ -0,0 +1,65 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_rendering_TextRendererBatcher_h
#define vtk_m_rendering_TextRendererBatcher_h
#include <string>
#include <vector>
#include <vtkm/rendering/BitmapFont.h>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/Color.h>
#include <vtkm/rendering/vtkm_rendering_export.h>
namespace vtkm
{
namespace rendering
{
class VTKM_RENDERING_EXPORT TextRendererBatcher
{
public:
using FontTextureType = vtkm::rendering::Canvas::FontTextureType;
using ScreenCoordsType = vtkm::Id4;
using TextureCoordsType = vtkm::Vec4f_32;
using ColorType = vtkm::Vec4f_32;
using ScreenCoordsArrayHandle = vtkm::cont::ArrayHandle<ScreenCoordsType>;
using TextureCoordsArrayHandle = vtkm::cont::ArrayHandle<TextureCoordsType>;
using ColorsArrayHandle = vtkm::cont::ArrayHandle<ColorType>;
using DepthsArrayHandle = vtkm::cont::ArrayHandle<vtkm::Float32>;
/*
VTKM_CONT
TextRendererBatcher();
*/
VTKM_CONT
TextRendererBatcher(const vtkm::rendering::Canvas::FontTextureType& fontTexture);
VTKM_CONT
void BatchText(const ScreenCoordsArrayHandle& screenCoords,
const TextureCoordsArrayHandle& textureCoords,
const vtkm::rendering::Color& color,
const vtkm::Float32& depth);
void Render(const vtkm::rendering::Canvas* canvas) const;
private:
vtkm::rendering::Canvas::FontTextureType FontTexture;
std::vector<ScreenCoordsType> ScreenCoords;
std::vector<TextureCoordsType> TextureCoords;
std::vector<ColorType> Colors;
std::vector<vtkm::Float32> Depths;
};
}
} // namespace vtkm::rendering
#endif // vtk_m_rendering_TextRendererBatcher_h

@ -197,10 +197,14 @@ void View::RenderAnnotations()
{
this->SetupForScreenSpace();
this->RenderScreenAnnotations();
this->GetCanvas().BeginTextRenderingBatch();
for (auto& textAnnotation : this->Internal->TextAnnotations)
{
textAnnotation->Render(this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
}
this->GetCanvas().EndTextRenderingBatch();
for (auto& additionalAnnotation : this->Internal->AdditionalAnnotations)
{
additionalAnnotation();

@ -99,6 +99,8 @@ public:
virtual void RenderScreenAnnotations() = 0;
virtual void RenderWorldAnnotations() = 0;
void RenderAnnotations();
void SaveAs(const std::string& fileName) const;
VTKM_CONT VTKM_DEPRECATED(1.6, "Use ClearTextAnnotations Instead") void ClearAnnotations();
@ -126,7 +128,6 @@ protected:
void SetupForScreenSpace(bool viewportClip = false);
void RenderAnnotations();
vtkm::rendering::Color AxisColor = vtkm::rendering::Color::white;
bool WorldAnnotationsEnabled = true;

@ -60,6 +60,8 @@ void View1D::RenderScreenAnnotations()
viewportBottom,
viewportTop);
this->GetCanvas().BeginTextRenderingBatch();
this->GetWorldAnnotator().BeginLineRenderingBatch();
this->HorizontalAxisAnnotation.SetColor(AxisColor);
this->HorizontalAxisAnnotation.SetScreenPosition(
viewportLeft, viewportBottom, viewportRight, viewportBottom);
@ -88,6 +90,9 @@ void View1D::RenderScreenAnnotations()
vtkm::rendering::TextAnnotation::VCenter);
this->VerticalAxisAnnotation.Render(
this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
this->GetWorldAnnotator().EndLineRenderingBatch();
this->GetCanvas().EndTextRenderingBatch();
}
void View1D::RenderColorLegendAnnotations()
@ -95,6 +100,8 @@ void View1D::RenderColorLegendAnnotations()
if (LegendEnabled)
{
this->Legend.Clear();
this->GetWorldAnnotator().BeginLineRenderingBatch();
this->GetCanvas().BeginTextRenderingBatch();
for (int i = 0; i < this->GetScene().GetNumberOfActors(); ++i)
{
vtkm::rendering::Actor act = this->GetScene().GetActor(i);
@ -110,6 +117,8 @@ void View1D::RenderColorLegendAnnotations()
}
this->Legend.SetLabelColor(this->GetCanvas().GetForegroundColor());
this->Legend.Render(this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
this->GetWorldAnnotator().EndLineRenderingBatch();
this->GetCanvas().EndTextRenderingBatch();
}
}

@ -56,6 +56,8 @@ void View2D::RenderScreenAnnotations()
viewportRight,
viewportBottom,
viewportTop);
this->GetCanvas().BeginTextRenderingBatch();
this->GetWorldAnnotator().BeginLineRenderingBatch();
this->HorizontalAxisAnnotation.SetColor(AxisColor);
this->HorizontalAxisAnnotation.SetScreenPosition(
viewportLeft, viewportBottom, viewportRight, viewportBottom);
@ -91,6 +93,8 @@ void View2D::RenderScreenAnnotations()
this->ColorBarAnnotation.Render(
this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
}
this->GetWorldAnnotator().EndLineRenderingBatch();
this->GetCanvas().EndTextRenderingBatch();
}
void View2D::RenderWorldAnnotations()

@ -47,17 +47,22 @@ void View3D::RenderScreenAnnotations()
{
if (this->GetScene().GetNumberOfActors() > 0)
{
this->GetCanvas().BeginTextRenderingBatch();
this->GetWorldAnnotator().BeginLineRenderingBatch();
//this->ColorBarAnnotation.SetAxisColor(vtkm::rendering::Color(1,1,1));
this->ColorBarAnnotation.SetFieldName(this->GetScene().GetActor(0).GetScalarField().GetName());
this->ColorBarAnnotation.SetRange(this->GetScene().GetActor(0).GetScalarRange(), 5);
this->ColorBarAnnotation.SetColorTable(this->GetScene().GetActor(0).GetColorTable());
this->ColorBarAnnotation.Render(
this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
this->GetWorldAnnotator().EndLineRenderingBatch();
this->GetCanvas().EndTextRenderingBatch();
}
}
void View3D::RenderWorldAnnotations()
{
this->GetCanvas().BeginTextRenderingBatch();
vtkm::Bounds bounds = this->GetScene().GetSpatialBounds();
vtkm::Float64 xmin = bounds.X.Min, xmax = bounds.X.Max;
vtkm::Float64 ymin = bounds.Y.Min, ymax = bounds.Y.Max;
@ -65,9 +70,11 @@ void View3D::RenderWorldAnnotations()
vtkm::Float64 dx = xmax - xmin, dy = ymax - ymin, dz = zmax - zmin;
vtkm::Float64 size = vtkm::Sqrt(dx * dx + dy * dy + dz * dz);
this->GetWorldAnnotator().BeginLineRenderingBatch();
this->BoxAnnotation.SetColor(Color(.5f, .5f, .5f));
this->BoxAnnotation.SetExtents(this->GetScene().GetSpatialBounds());
this->BoxAnnotation.Render(this->GetCamera(), this->GetWorldAnnotator());
this->GetWorldAnnotator().EndLineRenderingBatch();
vtkm::Vec3f_32 lookAt = this->GetCamera().GetLookAt();
vtkm::Vec3f_32 position = this->GetCamera().GetPosition();
@ -86,6 +93,7 @@ void View3D::RenderWorldAnnotations()
vtkm::Float64 yrel = vtkm::Abs(dy) / size;
vtkm::Float64 zrel = vtkm::Abs(dz) / size;
this->GetWorldAnnotator().BeginLineRenderingBatch();
this->XAxisAnnotation.SetAxis(0);
this->XAxisAnnotation.SetColor(AxisColor);
this->XAxisAnnotation.SetTickInvert(xtest, ytest, ztest);
@ -121,6 +129,9 @@ void View3D::RenderWorldAnnotations()
this->ZAxisAnnotation.SetLabelFontOffset(vtkm::Float32(size / 15.f));
this->ZAxisAnnotation.SetMoreOrLessTickAdjustment(zrel < .3 ? -1 : 0);
this->ZAxisAnnotation.Render(this->GetCamera(), this->GetWorldAnnotator(), this->GetCanvas());
this->GetWorldAnnotator().EndLineRenderingBatch();
this->GetCanvas().EndTextRenderingBatch();
}
}
} // namespace vtkm::rendering

@ -47,6 +47,7 @@ public:
private:
// 3D-specific annotations
vtkm::rendering::LineRendererBatcher LineBatcher;
vtkm::rendering::BoundingBoxAnnotation BoxAnnotation;
vtkm::rendering::AxisAnnotation3D XAxisAnnotation;
vtkm::rendering::AxisAnnotation3D YAxisAnnotation;

@ -32,10 +32,24 @@ void WorldAnnotator::AddLine(const vtkm::Vec3f_64& point0,
{
vtkm::Matrix<vtkm::Float32, 4, 4> transform =
vtkm::MatrixMultiply(Canvas->GetProjection(), Canvas->GetModelView());
LineRenderer renderer(Canvas, transform);
vtkm::rendering::WorldAnnotator* self = const_cast<vtkm::rendering::WorldAnnotator*>(this);
LineRenderer renderer(Canvas, transform, &(self->LineBatcher));
renderer.RenderLine(point0, point1, lineWidth, color);
}
void WorldAnnotator::BeginLineRenderingBatch() const
{
vtkm::rendering::WorldAnnotator* self = const_cast<vtkm::rendering::WorldAnnotator*>(this);
self->LineBatcher = vtkm::rendering::LineRendererBatcher();
}
void WorldAnnotator::EndLineRenderingBatch() const
{
vtkm::rendering::WorldAnnotator* self = const_cast<vtkm::rendering::WorldAnnotator*>(this);
vtkm::rendering::Canvas* canvas = const_cast<vtkm::rendering::Canvas*>(this->Canvas);
self->LineBatcher.Render(canvas);
}
void WorldAnnotator::AddText(const vtkm::Vec3f_32& origin,
const vtkm::Vec3f_32& right,
const vtkm::Vec3f_32& up,

@ -15,6 +15,7 @@
#include <vtkm/Types.h>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/Color.h>
#include <vtkm/rendering/LineRendererBatcher.h>
namespace vtkm
{
@ -51,6 +52,12 @@ public:
vtkm::make_Vec(x0, y0, z0), vtkm::make_Vec(x1, y1, z1), lineWidth, color, inFront);
}
VTKM_CONT
void BeginLineRenderingBatch() const;
VTKM_CONT
void EndLineRenderingBatch() const;
virtual void AddText(const vtkm::Vec3f_32& origin,
const vtkm::Vec3f_32& right,
const vtkm::Vec3f_32& up,
@ -87,6 +94,7 @@ public:
private:
const vtkm::rendering::Canvas* Canvas;
vtkm::rendering::LineRendererBatcher LineBatcher;
};
}
} //namespace vtkm::rendering

@ -8,56 +8,188 @@
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/source/Oscillator.h>
#include <vtkm/worklet/WorkletMapField.h>
namespace vtkm
{
namespace source
{
namespace internal
{
struct Oscillator
{
vtkm::Vec3f Center;
vtkm::FloatDefault Radius;
vtkm::FloatDefault Omega;
vtkm::FloatDefault Zeta;
};
class OscillatorSource : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldOut);
typedef _2 ExecutionSignature(_1);
VTKM_CONT
void AddPeriodic(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
if (this->PeriodicOscillators.GetNumberOfComponents() < MAX_OSCILLATORS)
{
this->PeriodicOscillators.Append(Oscillator{ { x, y, z }, radius, omega, zeta });
}
}
VTKM_CONT
void AddDamped(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
if (this->DampedOscillators.GetNumberOfComponents() < MAX_OSCILLATORS)
{
this->DampedOscillators.Append(Oscillator{ { x, y, z }, radius, omega, zeta });
}
}
VTKM_CONT
void AddDecaying(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
if (this->DecayingOscillators.GetNumberOfComponents() < MAX_OSCILLATORS)
{
this->DecayingOscillators.Append(Oscillator{ { x, y, z }, radius, omega, zeta });
}
}
VTKM_CONT
void SetTime(vtkm::FloatDefault time) { this->Time = time; }
VTKM_EXEC
vtkm::FloatDefault operator()(const vtkm::Vec3f& vec) const
{
vtkm::UInt8 oIdx;
vtkm::FloatDefault t0, t, result = 0;
const internal::Oscillator* oscillator;
t0 = 0.0;
t = vtkm::FloatDefault(this->Time * 2 * 3.14159265358979323846);
// Compute damped
for (oIdx = 0; oIdx < this->DampedOscillators.GetNumberOfComponents(); oIdx++)
{
oscillator = &this->DampedOscillators[oIdx];
vtkm::Vec3f delta = oscillator->Center - vec;
vtkm::FloatDefault dist2 = dot(delta, delta);
vtkm::FloatDefault dist_damp =
vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::FloatDefault phi = vtkm::ACos(oscillator->Zeta);
vtkm::FloatDefault val = vtkm::FloatDefault(
1. -
vtkm::Exp(-oscillator->Zeta * oscillator->Omega * t0) *
(vtkm::Sin(vtkm::Sqrt(1 - oscillator->Zeta * oscillator->Zeta) * oscillator->Omega * t +
phi) /
vtkm::Sin(phi)));
result += val * dist_damp;
}
// Compute decaying
for (oIdx = 0; oIdx < this->DecayingOscillators.GetNumberOfComponents(); oIdx++)
{
oscillator = &this->DecayingOscillators[oIdx];
t = t0 + 1 / oscillator->Omega;
vtkm::Vec3f delta = oscillator->Center - vec;
vtkm::FloatDefault dist2 = dot(delta, delta);
vtkm::FloatDefault dist_damp =
vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::FloatDefault val = vtkm::Sin(t / oscillator->Omega) / (oscillator->Omega * t);
result += val * dist_damp;
}
// Compute periodic
for (oIdx = 0; oIdx < this->PeriodicOscillators.GetNumberOfComponents(); oIdx++)
{
oscillator = &this->PeriodicOscillators[oIdx];
t = t0 + 1 / oscillator->Omega;
vtkm::Vec3f delta = oscillator->Center - vec;
vtkm::FloatDefault dist2 = dot(delta, delta);
vtkm::FloatDefault dist_damp =
vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::FloatDefault val = vtkm::Sin(t / oscillator->Omega);
result += val * dist_damp;
}
// We are done...
return result;
}
private:
static constexpr vtkm::IdComponent MAX_OSCILLATORS = 10;
vtkm::VecVariable<internal::Oscillator, MAX_OSCILLATORS> PeriodicOscillators;
vtkm::VecVariable<internal::Oscillator, MAX_OSCILLATORS> DampedOscillators;
vtkm::VecVariable<internal::Oscillator, MAX_OSCILLATORS> DecayingOscillators;
vtkm::FloatDefault Time;
}; // OscillatorSource
} // internal
//-----------------------------------------------------------------------------
Oscillator::Oscillator(vtkm::Id3 dims)
: Dims(dims)
, Worklet()
, Worklet(std::make_unique<internal::OscillatorSource>())
{
}
Oscillator::~Oscillator() = default;
//-----------------------------------------------------------------------------
void Oscillator::SetTime(vtkm::Float64 time)
void Oscillator::SetTime(vtkm::FloatDefault time)
{
this->Worklet.SetTime(time);
this->Worklet->SetTime(time);
}
//-----------------------------------------------------------------------------
void Oscillator::AddPeriodic(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
void Oscillator::AddPeriodic(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
this->Worklet.AddPeriodic(x, y, z, radius, omega, zeta);
this->Worklet->AddPeriodic(x, y, z, radius, omega, zeta);
}
//-----------------------------------------------------------------------------
void Oscillator::AddDamped(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
void Oscillator::AddDamped(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
this->Worklet.AddDamped(x, y, z, radius, omega, zeta);
this->Worklet->AddDamped(x, y, z, radius, omega, zeta);
}
//-----------------------------------------------------------------------------
void Oscillator::AddDecaying(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
void Oscillator::AddDecaying(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta)
{
this->Worklet.AddDecaying(x, y, z, radius, omega, zeta);
this->Worklet->AddDecaying(x, y, z, radius, omega, zeta);
}
@ -68,8 +200,9 @@ vtkm::cont::DataSet Oscillator::Execute() const
vtkm::cont::DataSet dataSet;
const vtkm::Id3 pdims{ this->Dims + vtkm::Id3{ 1, 1, 1 } };
vtkm::cont::CellSetStructured<3> cellSet;
cellSet.SetPointDimensions(this->Dims);
cellSet.SetPointDimensions(pdims);
dataSet.SetCellSet(cellSet);
const vtkm::Vec3f origin(0.0f, 0.0f, 0.0f);
@ -77,15 +210,12 @@ vtkm::cont::DataSet Oscillator::Execute() const
1.0f / static_cast<vtkm::FloatDefault>(this->Dims[1]),
1.0f / static_cast<vtkm::FloatDefault>(this->Dims[2]));
const vtkm::Id3 pdims{ this->Dims + vtkm::Id3{ 1, 1, 1 } };
vtkm::cont::ArrayHandleUniformPointCoordinates coordinates(pdims, origin, spacing);
dataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", coordinates));
vtkm::cont::ArrayHandle<vtkm::Float64> outArray;
//todo, we need to use the policy to determine the valid conversions
//that the dispatcher should do
this->Invoke(this->Worklet, coordinates, outArray);
vtkm::cont::ArrayHandle<vtkm::FloatDefault> outArray;
this->Invoke(*(this->Worklet), coordinates, outArray);
dataSet.AddField(vtkm::cont::make_FieldPoint("oscillating", outArray));
return dataSet;

@ -11,12 +11,15 @@
#define vtk_m_source_OscillatorSource_h
#include <vtkm/source/Source.h>
#include <vtkm/worklet/OscillatorSource.h>
namespace vtkm
{
namespace source
{
namespace internal
{
class OscillatorSource;
}
/**\brief An analytical, time-varying uniform dataset with a point based array
*
@ -33,38 +36,44 @@ public:
VTKM_CONT
Oscillator(vtkm::Id3 dims);
// We can not declare default destructor here since compiler does not know how
// to create one for the Worklet at this point yet. However, the implementation
// in Oscillator.cxx does have ~Oscillator() = default;
VTKM_CONT
void SetTime(vtkm::Float64 time);
~Oscillator();
VTKM_CONT
void AddPeriodic(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta);
void SetTime(vtkm::FloatDefault time);
VTKM_CONT
void AddDamped(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta);
void AddPeriodic(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta);
VTKM_CONT
void AddDecaying(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta);
void AddDamped(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta);
VTKM_CONT
void AddDecaying(vtkm::FloatDefault x,
vtkm::FloatDefault y,
vtkm::FloatDefault z,
vtkm::FloatDefault radius,
vtkm::FloatDefault omega,
vtkm::FloatDefault zeta);
VTKM_CONT vtkm::cont::DataSet Execute() const;
private:
vtkm::Id3 Dims;
vtkm::worklet::OscillatorSource Worklet;
std::unique_ptr<internal::OscillatorSource> Worklet;
};
}
}

@ -9,6 +9,7 @@
##============================================================================
set(unit_tests
UnitTestOscillatorSource.cxx
UnitTestTangleSource.cxx
UnitTestWaveletSource.cxx
)
@ -16,5 +17,4 @@ set(unit_tests
vtkm_unit_tests(
SOURCES ${unit_tests}
LIBRARIES vtkm_source
ALL_BACKENDS
)

@ -0,0 +1,66 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/source/Oscillator.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/testing/Testing.h>
void OscillatorSourceTest()
{
vtkm::cont::Timer timer;
timer.Start();
vtkm::source::Oscillator source(vtkm::Id3{ 20, 20, 20 });
source.SetTime(0.5);
source.AddDamped(0.25f, 0.25f, 0.25f, 0.5f, 0.1f, 0.2f);
source.AddDecaying(0.5f, 0.5f, 0.5f, 0.35f, 0.2f, 0.1f);
source.AddPeriodic(0.6f, 0.2f, 0.7f, 0.15f, 0.1f, 0.2f);
vtkm::cont::DataSet ds = source.Execute();
double time = timer.GetElapsedTime();
std::cout << "Default oscillator took " << time << "s.\n";
{
auto coords = ds.GetCoordinateSystem("coordinates");
auto data = coords.GetData();
VTKM_TEST_ASSERT(test_equal(data.GetNumberOfValues(), 9261), "Incorrect number of points.");
}
{
auto cells = ds.GetCellSet();
VTKM_TEST_ASSERT(test_equal(cells.GetNumberOfCells(), 8000), "Incorrect number of cells.");
}
// Spot check some node scalars
{
using ScalarHandleType = vtkm::cont::ArrayHandle<vtkm::FloatDefault>;
auto field = ds.GetPointField("oscillating");
auto dynData = field.GetData();
VTKM_TEST_ASSERT(dynData.IsType<ScalarHandleType>(), "Invalid scalar handle type.");
ScalarHandleType handle = dynData.AsArrayHandle<ScalarHandleType>();
auto data = handle.ReadPortal();
VTKM_TEST_ASSERT(test_equal(data.GetNumberOfValues(), 9261), "Incorrect number of scalars.");
VTKM_TEST_ASSERT(test_equal(data.Get(0), -0.0163996), "Incorrect scalar value.");
VTKM_TEST_ASSERT(test_equal(data.Get(16), -0.0182232), "Incorrect scalar value.");
VTKM_TEST_ASSERT(test_equal(data.Get(21), -0.0181952), "Incorrect scalar value.");
VTKM_TEST_ASSERT(test_equal(data.Get(3110), -0.0404135), "Incorrect scalar value.");
}
}
int UnitTestOscillatorSource(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(OscillatorSourceTest, argc, argv);
}

@ -10,7 +10,6 @@
#include <vtkm/source/Wavelet.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/testing/Testing.h>
void WaveletSourceTest()

@ -57,7 +57,6 @@ set(headers
OrientNormals.h
OrientPointNormals.h
OrientPointAndCellNormals.h
OscillatorSource.h
ParticleAdvection.h
PointAverage.h
PointElevation.h

@ -1,187 +0,0 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_worklet_OscillatorSource_h
#define vtk_m_worklet_OscillatorSource_h
#include <vtkm/Math.h>
#include <vtkm/worklet/WorkletMapField.h>
#define MAX_OSCILLATORS 10
namespace vtkm
{
namespace worklet
{
namespace internal
{
struct Oscillator
{
void Set(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
{
this->Center[0] = x;
this->Center[1] = y;
this->Center[2] = z;
this->Radius = radius;
this->Omega = omega;
this->Zeta = zeta;
}
vtkm::Vec3f_64 Center;
vtkm::Float64 Radius;
vtkm::Float64 Omega;
vtkm::Float64 Zeta;
};
}
class OscillatorSource : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldOut);
typedef _2 ExecutionSignature(_1);
VTKM_CONT
OscillatorSource()
: NumberOfPeriodics(0)
, NumberOfDamped(0)
, NumberOfDecaying(0)
{
}
VTKM_CONT
void AddPeriodic(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
{
if (this->NumberOfPeriodics < MAX_OSCILLATORS)
{
this->PeriodicOscillators[this->NumberOfPeriodics].Set(x, y, z, radius, omega, zeta);
this->NumberOfPeriodics++;
}
}
VTKM_CONT
void AddDamped(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
{
if (this->NumberOfDamped < MAX_OSCILLATORS)
{
this->DampedOscillators[this->NumberOfDamped * 6].Set(x, y, z, radius, omega, zeta);
this->NumberOfDamped++;
}
}
VTKM_CONT
void AddDecaying(vtkm::Float64 x,
vtkm::Float64 y,
vtkm::Float64 z,
vtkm::Float64 radius,
vtkm::Float64 omega,
vtkm::Float64 zeta)
{
if (this->NumberOfDecaying < MAX_OSCILLATORS)
{
this->DecayingOscillators[this->NumberOfDecaying * 6].Set(x, y, z, radius, omega, zeta);
this->NumberOfDecaying++;
}
}
VTKM_CONT
void SetTime(vtkm::Float64 time) { this->Time = time; }
VTKM_EXEC
vtkm::Float64 operator()(const vtkm::Vec3f_64& vec) const
{
vtkm::UInt8 oIdx;
vtkm::Float64 t0, t, result = 0;
const internal::Oscillator* oscillator;
t0 = 0.0;
t = this->Time * 2 * 3.14159265358979323846;
// Compute damped
for (oIdx = 0; oIdx < this->NumberOfDamped; oIdx++)
{
oscillator = &this->DampedOscillators[oIdx];
vtkm::Vec3f_64 delta = oscillator->Center - vec;
vtkm::Float64 dist2 = dot(delta, delta);
vtkm::Float64 dist_damp = vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::Float64 phi = vtkm::ACos(oscillator->Zeta);
vtkm::Float64 val = 1. -
vtkm::Exp(-oscillator->Zeta * oscillator->Omega * t0) *
(vtkm::Sin(vtkm::Sqrt(1 - oscillator->Zeta * oscillator->Zeta) * oscillator->Omega * t +
phi) /
vtkm::Sin(phi));
result += val * dist_damp;
}
// Compute decaying
for (oIdx = 0; oIdx < this->NumberOfDecaying; oIdx++)
{
oscillator = &this->DecayingOscillators[oIdx];
t = t0 + 1 / oscillator->Omega;
vtkm::Vec3f_64 delta = oscillator->Center - vec;
vtkm::Float64 dist2 = dot(delta, delta);
vtkm::Float64 dist_damp = vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::Float64 val = vtkm::Sin(t / oscillator->Omega) / (oscillator->Omega * t);
result += val * dist_damp;
}
// Compute periodic
for (oIdx = 0; oIdx < this->NumberOfPeriodics; oIdx++)
{
oscillator = &this->PeriodicOscillators[oIdx];
t = t0 + 1 / oscillator->Omega;
vtkm::Vec3f_64 delta = oscillator->Center - vec;
vtkm::Float64 dist2 = dot(delta, delta);
vtkm::Float64 dist_damp = vtkm::Exp(-dist2 / (2 * oscillator->Radius * oscillator->Radius));
vtkm::Float64 val = vtkm::Sin(t / oscillator->Omega);
result += val * dist_damp;
}
// We are done...
return result;
}
template <typename T>
VTKM_EXEC vtkm::Float64 operator()(const vtkm::Vec<T, 3>& vec) const
{
return (*this)(vtkm::make_Vec(static_cast<vtkm::Float64>(vec[0]),
static_cast<vtkm::Float64>(vec[1]),
static_cast<vtkm::Float64>(vec[2])));
}
private:
vtkm::Vec<internal::Oscillator, MAX_OSCILLATORS> PeriodicOscillators;
vtkm::Vec<internal::Oscillator, MAX_OSCILLATORS> DampedOscillators;
vtkm::Vec<internal::Oscillator, MAX_OSCILLATORS> DecayingOscillators;
vtkm::UInt8 NumberOfPeriodics;
vtkm::UInt8 NumberOfDamped;
vtkm::UInt8 NumberOfDecaying;
vtkm::Float64 Time;
};
}
} // namespace vtkm
#endif // vtk_m_worklet_PointElevation_h

@ -30,21 +30,36 @@ class ExecutionVelocityField
{
public:
using FieldPortalType = typename FieldArrayType::ReadPortalType;
using Association = vtkm::cont::Field::Association;
VTKM_CONT
ExecutionVelocityField(FieldArrayType velocityValues,
ExecutionVelocityField(const FieldArrayType& velocityValues,
const Association assoc,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
: VelocityValues(velocityValues.PrepareForInput(device, token))
, Assoc(assoc)
{
}
VTKM_EXEC Association GetAssociation() const { return this->Assoc; }
VTKM_EXEC void GetValue(const vtkm::Id cellId, vtkm::VecVariable<vtkm::Vec3f, 2>& value) const
{
VTKM_ASSERT(this->Assoc == Association::CELL_SET);
vtkm::Vec3f velocity = VelocityValues.Get(cellId);
value = vtkm::make_Vec(velocity);
}
VTKM_EXEC void GetValue(const vtkm::VecVariable<vtkm::Id, 8>& indices,
const vtkm::Id vertices,
const vtkm::Vec3f& parametric,
const vtkm::UInt8 cellShape,
vtkm::VecVariable<vtkm::Vec3f, 2>& value) const
{
VTKM_ASSERT(this->Assoc == Association::POINTS);
vtkm::Vec3f velocityInterp;
vtkm::VecVariable<vtkm::Vec3f, 8> velocities;
for (vtkm::IdComponent i = 0; i < vertices; i++)
@ -55,6 +70,7 @@ public:
private:
FieldPortalType VelocityValues;
Association Assoc;
};
template <typename FieldArrayType>
@ -62,23 +78,39 @@ class ExecutionElectroMagneticField
{
public:
using FieldPortalType = typename FieldArrayType::ReadPortalType;
using Association = vtkm::cont::Field::Association;
VTKM_CONT
ExecutionElectroMagneticField(FieldArrayType electricValues,
FieldArrayType magneticValues,
ExecutionElectroMagneticField(const FieldArrayType& electricValues,
const FieldArrayType& magneticValues,
const Association assoc,
vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token)
: ElectricValues(electricValues.PrepareForInput(device, token))
, MagneticValues(magneticValues.PrepareForInput(device, token))
, Assoc(assoc)
{
}
VTKM_EXEC Association GetAssociation() const { return this->Assoc; }
VTKM_EXEC void GetValue(const vtkm::Id cellId, vtkm::VecVariable<vtkm::Vec3f, 2>& value) const
{
VTKM_ASSERT(this->Assoc == Association::CELL_SET);
auto electric = this->ElectricValues.Get(cellId);
auto magnetic = this->MagneticValues.Get(cellId);
value = vtkm::make_Vec(electric, magnetic);
}
VTKM_EXEC void GetValue(const vtkm::VecVariable<vtkm::Id, 8>& indices,
const vtkm::Id vertices,
const vtkm::Vec3f& parametric,
const vtkm::UInt8 cellShape,
vtkm::VecVariable<vtkm::Vec3f, 2>& value) const
{
VTKM_ASSERT(this->Assoc == Association::POINTS);
vtkm::Vec3f electricInterp, magneticInterp;
vtkm::VecVariable<vtkm::Vec3f, 8> electric;
vtkm::VecVariable<vtkm::Vec3f, 8> magnetic;
@ -95,6 +127,7 @@ public:
private:
FieldPortalType ElectricValues;
FieldPortalType MagneticValues;
Association Assoc;
};
template <typename FieldArrayType>
@ -102,6 +135,7 @@ class VelocityField : public vtkm::cont::ExecutionObjectBase
{
public:
using ExecutionType = ExecutionVelocityField<FieldArrayType>;
using Association = vtkm::cont::Field::Association;
VTKM_CONT
VelocityField() = default;
@ -109,18 +143,29 @@ public:
VTKM_CONT
VelocityField(const FieldArrayType& fieldValues)
: FieldValues(fieldValues)
, Assoc(vtkm::cont::Field::Association::POINTS)
{
}
VTKM_CONT
VelocityField(const FieldArrayType& fieldValues, const Association assoc)
: FieldValues(fieldValues)
, Assoc(assoc)
{
if (assoc == Association::ANY || assoc == Association::WHOLE_MESH)
throw("Unsupported field association");
}
VTKM_CONT
const ExecutionType PrepareForExecution(vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token) const
{
return ExecutionType(this->FieldValues, device, token);
return ExecutionType(this->FieldValues, this->Assoc, device, token);
}
private:
FieldArrayType FieldValues;
Association Assoc;
};
template <typename FieldArrayType>
@ -128,6 +173,7 @@ class ElectroMagneticField : public vtkm::cont::ExecutionObjectBase
{
public:
using ExecutionType = ExecutionElectroMagneticField<FieldArrayType>;
using Association = vtkm::cont::Field::Association;
VTKM_CONT
ElectroMagneticField() = default;
@ -136,6 +182,17 @@ public:
ElectroMagneticField(const FieldArrayType& electricField, const FieldArrayType& magneticField)
: ElectricField(electricField)
, MagneticField(magneticField)
, Assoc(vtkm::cont::Field::Association::POINTS)
{
}
VTKM_CONT
ElectroMagneticField(const FieldArrayType& electricField,
const FieldArrayType& magneticField,
const Association assoc)
: ElectricField(electricField)
, MagneticField(magneticField)
, Assoc(assoc)
{
}
@ -143,12 +200,13 @@ public:
const ExecutionType PrepareForExecution(vtkm::cont::DeviceAdapterId device,
vtkm::cont::Token& token) const
{
return ExecutionType(this->ElectricField, this->MagneticField, device, token);
return ExecutionType(this->ElectricField, this->MagneticField, this->Assoc, device, token);
}
private:
FieldArrayType ElectricField;
FieldArrayType MagneticField;
Association Assoc;
};
} // namespace particleadvection

@ -125,8 +125,17 @@ public:
vtkm::IdComponent nVerts;
vtkm::VecVariable<vtkm::Id, 8> ptIndices;
vtkm::VecVariable<vtkm::Vec3f, 8> fieldValues;
this->InterpolationHelper.GetCellInfo(cellId, cellShape, nVerts, ptIndices);
this->Field.GetValue(ptIndices, nVerts, parametric, cellShape, out);
if (this->Field.GetAssociation() == vtkm::cont::Field::Association::POINTS)
{
this->InterpolationHelper.GetCellInfo(cellId, cellShape, nVerts, ptIndices);
this->Field.GetValue(ptIndices, nVerts, parametric, cellShape, out);
}
else if (this->Field.GetAssociation() == vtkm::cont::Field::Association::CELL_SET)
{
this->Field.GetValue(cellId, out);
}
status.SetOk();
}