chromium/third_party/dawn/src/dawn/native/SubresourceStorage.h

// Copyright 2020 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef SRC_DAWN_NATIVE_SUBRESOURCESTORAGE_H_
#define SRC_DAWN_NATIVE_SUBRESOURCESTORAGE_H_

#include <array>
#include <limits>
#include <memory>
#include <type_traits>
#include <vector>

#include "dawn/common/Assert.h"
#include "dawn/common/TypeTraits.h"
#include "dawn/native/EnumMaskIterator.h"
#include "dawn/native/Error.h"
#include "dawn/native/Subresource.h"

namespace dawn::native {

// SubresourceStorage<T> acts like a simple map from subresource (aspect, layer, level) to a
// value of type T except that it tries to compress similar subresources so that algorithms
// can act on a whole range of subresources at once if they have the same state.
//
// For example a very common case to optimize for is the tracking of the usage of texture
// subresources inside a render pass: the vast majority of texture views will select the whole
// texture while a small minority will select a sub-range. We want to optimize the common case
// by setting and checking a single "usage" value when a full subresource is used but at the
// same time allow per-subresource data when needed.
//
// Another example is barrier tracking per-subresource in the backends: it will often happen
// that during texture upload each mip level will have a different "barrier state". However
// when the texture is fully uploaded and after it is used for sampling (with a full view) for
// the first time, the barrier state will likely be the same across all the subresources.
// That's why some form of "recompression" of subresource state must be possibe.
//
// In order to keep the implementation details private and to avoid iterator-hell, this
// container uses a more functional approach of calling a closure on the interesting ranges.
// This is for example how to look at the state of all subresources.
//
//   subresources.Iterate([](const SubresourceRange& range, const T& data) {
//      // Do something with the knowledge that all the subresources in `range` have value
//      // `data`.
//   });
//
// SubresourceStorage internally tracks compression state per aspect and then per layer of each
// aspect. This means that a 2-aspect texture can have the following compression state:
//
//  - Aspect 0 is fully compressed.
//  - Aspect 1 is partially compressed:
//    - Aspect 1 layer 3 is decompressed.
//    - Aspect 1 layer 0-2 and 4-42 are compressed.
//
// A useful model to reason about SubresourceStorage is to represent is as a tree:
//
//  - SubresourceStorage is the root.
//    |-> Nodes 1 deep represent each aspect. If an aspect is compressed, its node doesn't have
//       any children because the data is constant across all of the subtree.
//      |-> Nodes 2 deep represent layers (for uncompressed aspects). If a layer is compressed,
//         its node doesn't have any children because the data is constant across all of the
//         subtree.
//        |-> Nodes 3 deep represent individial mip levels (for uncompressed layers).
//
// The concept of recompression is the removal of all child nodes of a non-leaf node when the
// data is constant across them. Decompression is the addition of child nodes to a leaf node
// and copying of its data to all its children.
//
// The choice of having secondary compression for array layers is to optimize for the cases
// where transfer operations are used to update specific layers of texture with render or
// transfer operations, while the rest is untouched. It seems much less likely that there
// would be operations that touch all Nth mips of a 2D array texture without touching the
// others.
//
// There are several hot code paths that create new SubresourceStorage like the tracking of
// resource usage per-pass. We don't want to allocate a container for the decompressed data
// unless we have to because it would dramatically lower performance. Instead
// SubresourceStorage contains an inline array that contains the per-aspect compressed data
// and only allocates a per-subresource on aspect decompression.
//
// T must be a copyable type that supports equality comparison with ==.
//
// The implementation of functions in this file can have a lot of control flow and corner cases
// so each modification should come with extensive tests and ensure 100% code coverage of the
// modified functions. See instructions at
// https://chromium.googlesource.com/chromium/src/+/main/docs/testing/code_coverage.md#local-coverage-script
// to run the test with code coverage. A command line that worked in the past (with the right
// GN args for the out/coverage directory in a Chromium checkout) is:
//
/*
   python tools/code_coverage/coverage.py dawn_unittests -b out/coverage -o out/report -c \
       "out/coverage/dawn_unittests --gtest_filter=SubresourceStorage\*" -f \
       third_party/dawn/src/dawn/native
*/
//
// TODO(crbug.com/dawn/836): Make the recompression optional, the calling code should know
// if recompression can happen or not in Update() and Merge()
template <typename T>
class SubresourceStorage {};

template <typename T>
SubresourceStorage<T>::SubresourceStorage(Aspect aspects,
                                          uint32_t arrayLayerCount,
                                          uint32_t mipLevelCount,
                                          const T& initialValue)
    :{}

template <typename T>
void SubresourceStorage<T>::Fill(const T& value) {}

template <typename T>
template <typename F>
void SubresourceStorage<T>::Update(const SubresourceRange& range, F&& updateFunc) {}

template <typename T>
template <typename U, typename F>
void SubresourceStorage<T>::Merge(const SubresourceStorage<U>& other, F&& mergeFunc) {}

template <typename T>
template <typename F, typename R>
R SubresourceStorage<T>::Iterate(F&& iterateFunc) const {}

template <typename T>
const T& SubresourceStorage<T>::Get(Aspect aspect, uint32_t arrayLayer, uint32_t mipLevel) const {}

template <typename T>
Aspect SubresourceStorage<T>::GetAspectsForTesting() const {}

template <typename T>
uint32_t SubresourceStorage<T>::GetArrayLayerCountForTesting() const {}

template <typename T>
uint32_t SubresourceStorage<T>::GetMipLevelCountForTesting() const {}

template <typename T>
bool SubresourceStorage<T>::IsAspectCompressedForTesting(Aspect aspect) const {}

template <typename T>
bool SubresourceStorage<T>::IsLayerCompressedForTesting(Aspect aspect, uint32_t layer) const {}

template <typename T>
void SubresourceStorage<T>::DecompressAspect(uint32_t aspectIndex) {}

template <typename T>
void SubresourceStorage<T>::RecompressAspect(uint32_t aspectIndex) {}

template <typename T>
void SubresourceStorage<T>::DecompressLayer(uint32_t aspectIndex, uint32_t layer) {}

template <typename T>
void SubresourceStorage<T>::RecompressLayer(uint32_t aspectIndex, uint32_t layer) {}

template <typename T>
SubresourceRange SubresourceStorage<T>::GetFullLayerRange(Aspect aspect, uint32_t layer) const {}

template <typename T>
bool& SubresourceStorage<T>::LayerCompressed(uint32_t aspectIndex, uint32_t layer) {}

template <typename T>
bool SubresourceStorage<T>::LayerCompressed(uint32_t aspectIndex, uint32_t layer) const {}

template <typename T>
T& SubresourceStorage<T>::DataInline(uint32_t aspectIndex) {}
template <typename T>
T& SubresourceStorage<T>::Data(uint32_t aspectIndex, uint32_t layer, uint32_t level) {}
template <typename T>
const T& SubresourceStorage<T>::DataInline(uint32_t aspectIndex) const {}
template <typename T>
const T& SubresourceStorage<T>::Data(uint32_t aspectIndex, uint32_t layer, uint32_t level) const {}

}  // namespace dawn::native

#endif  // SRC_DAWN_NATIVE_SUBRESOURCESTORAGE_H_