<commit_before>/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>
#include <utility>

#include "llvm/ADT/SmallVector.h"
#include "mlir-hlo/Dialect/gml_st/IR/gml_st_ops.h"
#include "mlir-hlo/Dialect/gml_st/transforms/fusion.h"
#include "mlir-hlo/Dialect/gml_st/transforms/linalg_utils.h"
#include "mlir-hlo/Dialect/gml_st/transforms/passes.h"
#include "mlir-hlo/Dialect/gml_st/transforms/tiling_interface_impl.h"
#include "mlir-hlo/Dialect/gml_st/transforms/transforms.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"

#define GEN_PASS_DEF_TILINGGPUWARPPASS
#include "mlir-hlo/Dialect/gml_st/transforms/passes.h.inc"

namespace mlir {
namespace gml_st {

namespace {

constexpr const char* kWarpDistributionLabel = "warp";
constexpr const char* kThreadDistributionLabel = "thread";

bool isWarpLevelOp(Operation* op) {
  if (!op) return false;
  auto parentPloop = op->getParentOfType<ParallelOp>();
  return parentPloop && parentPloop.getDistributionType() &&
         *parentPloop.getDistributionType() == kWarpDistributionLabel;
}

struct TilingCwisePattern : OpRewritePattern<linalg::GenericOp> {
  using OpRewritePattern<linalg::GenericOp>::OpRewritePattern;

  LogicalResult matchAndRewrite(linalg::GenericOp genericOp,
                                PatternRewriter& rewriter) const override {
    if (hasTransformationAttr(genericOp)) {
      return rewriter.notifyMatchFailure(genericOp, "already transformed");
    }

    // Match only cwise `linalg.generic` ops on the shape 1x?.
    auto genericOpTy =
        genericOp.getResultTypes().front().dyn_cast<RankedTensorType>();
    if (!isCwiseGenericOp(genericOp) || !genericOpTy ||
        genericOpTy.getRank() != 2 || genericOpTy.getDimSize(0) != 1) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "not a cwise op on tensor<1x?>");
    }

    // Only tile root ops on the warp level.
    if (!isWarpLevelOp(genericOp) || !genericOp->hasOneUse() ||
        !llvm::isa<SetYieldOp>(*genericOp->getUsers().begin())) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "not on warp level root op");
    }

    // Constants and attributes.
    Location loc = genericOp.getLoc();
    Value c0 = rewriter.create<arith::ConstantIndexOp>(loc, 0);
    Value c1 = rewriter.create<arith::ConstantIndexOp>(loc, 1);
    Value cWarpSize = rewriter.create<arith::ConstantIndexOp>(loc, 32);
    Value cWarpSizeMinusOne = rewriter.create<arith::ConstantIndexOp>(loc, 31);
    Attribute zeroAttr = rewriter.getIndexAttr(0);
    Attribute oneAttr = rewriter.getIndexAttr(1);
    Attribute warpSizeAttr = rewriter.getIndexAttr(32);
    StringAttr threadDist = rewriter.getStringAttr(kThreadDistributionLabel);

    // Create `gml_st.parallel` loop to distribute among lanes.
    Value init = genericOp.getOutputs().front();
    Value genericOpResult = genericOp.getResults().front();
    Value dimSize =
        rewriter.createOrFold<tensor::DimOp>(loc, genericOpResult, c1);
    Value dimSizePlusWarpSizeMinusOne =
        rewriter.createOrFold<arith::AddIOp>(loc, dimSize, cWarpSizeMinusOne);
    auto ploop = rewriter.create<gml_st::ParallelOp>(
        loc, genericOpTy, c0, cWarpSize, c1, threadDist,
        [&](OpBuilder& b, Location loc, ValueRange ivs) {
          // Compute the lane tile with a stride of `warpSize`. This tile
          // defines the subset of the result that is produced by the lane.
          // The `laneId` defines the initial offset into the tensor. The
          // remaining length to be addressed by the lane is
          //     `dimSize` - `laneId`.
          // With a stride of `warpSize`, every lane addresses a total of
          //     ceil((`dimSize` - `laneId`) / `cWarpSize`)
          //     = (`dimSize` + `cWarpSize` - 1 - `laneId`) / `cWarpSize`
          // elements.
          Value laneId = ivs.front();
          Value laneTileSize = b.create<arith::DivUIOp>(
              loc,
              b.create<arith::SubIOp>(loc, dimSizePlusWarpSizeMinusOne, laneId),
              cWarpSize);
          Value laneTile = b.createOrFold<gml_st::TileOp>(
              loc, SmallVector<OpFoldResult>{zeroAttr, laneId},
              SmallVector<OpFoldResult>{oneAttr, laneTileSize},
              SmallVector<OpFoldResult>{oneAttr, warpSizeAttr});

          // Create `gml_st.for` loop to iterate over the lane's tile.
          Type elemTy = genericOpTy.getElementType();
          auto sloopTy =
              RankedTensorType::get({1, ShapedType::kDynamicSize}, elemTy);
          Value laneInit = b.create<gml_st::MaterializeOp>(loc, init, laneTile);
          auto sloop = b.create<gml_st::ForOp>(
              loc, sloopTy, c0, laneTileSize, c1, laneInit,
              [&](OpBuilder& b, Location loc, ValueRange ivs, ValueRange aggr) {
                // Create the iteration tile. This specifies the scalar subset
                // in the warp-level operands.
                Value i = ivs.front();
                Value iterTileOffset =
                    b.create<arith::MulIOp>(loc, i, cWarpSize);
                Value iterTile = b.create<gml_st::TileOp>(
                    loc, SmallVector<OpFoldResult>{zeroAttr, iterTileOffset},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr});

                // Materialize scalar subsets per operand.
                SmallVector<Value> iterOperands =
                    llvm::to_vector(llvm::map_range(
                        genericOp.getInputs(), [&](Value arg) -> Value {
                          return b.create<gml_st::MaterializeOp>(loc, elemTy,
                                                                 arg, iterTile);
                        }));

                // Create scalar computation from `linalg.generic` body by (i)
                // mapping its block arguments to the newly materialized
                // scalar operands, and (ii) cloning the body.
                BlockAndValueMapping bvm;
                for (const auto& [blockArg, iterOperand] : llvm::zip(
                         genericOp.getBlock()->getArguments(), iterOperands)) {
                  bvm.map(blockArg, iterOperand);
                }
                for (auto& innerop :
                     genericOp.getBody()->without_terminator()) {
                  rewriter.clone(innerop, bvm);
                }

                // Yield iteration result.
                Value iterResult = bvm.lookup(genericOp.getBody()
                                                  ->getTerminator()
                                                  ->getOperands()
                                                  .front());
                Value iterTileInLaneTile = b.create<gml_st::TileOp>(
                    loc, SmallVector<OpFoldResult>{zeroAttr, i},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr});
                b.create<gml_st::SetYieldOp>(loc, iterResult, aggr,
                                             iterTileInLaneTile);
              });
          b.create<gml_st::SetYieldOp>(loc, sloop.getResults().front(), init,
                                       laneTile);
        });

    rewriter.replaceOp(genericOp, ploop.getResults());
    return success();
  }
};

struct TilingReductionPattern : OpRewritePattern<linalg::GenericOp> {
  using OpRewritePattern<linalg::GenericOp>::OpRewritePattern;

  LogicalResult matchAndRewrite(linalg::GenericOp genericOp,
                                PatternRewriter& rewriter) const override {
    if (gml_st::hasTransformationAttr(genericOp)) return failure();

    // Match only reduction on the warp level.
    if (!isWarpLevelOp(genericOp)) return failure();

    // Match only if it's a linalg.generic tensor<1x?xf32> -> tensor<1xf32> with
    // iterator_types = ["parallel", "reduction"].
    auto itTypes = llvm::to_vector(
        genericOp.getIteratorTypes().getAsValueRange<StringAttr>());
    if (itTypes.size() != 2 || itTypes[0] != getParallelIteratorTypeName() ||
        itTypes[1] != getReductionIteratorTypeName()) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "Expected ['parallel', 'reduction']");
    }
    if (genericOp.getNumInputs() != 1 || genericOp.getNumOutputs() != 1) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "Expected single input and output");
    }
    auto input = genericOp.getInputs().front();
    auto output = genericOp.getOutputs().front();
    auto outType = output.getType().dyn_cast<TensorType>();
    constexpr int kWarpSize = 32;

    Location loc = genericOp->getLoc();
    Value c0 = rewriter.create<arith::ConstantIndexOp>(loc, 0);
    Value c1 = rewriter.create<arith::ConstantIndexOp>(loc, 1);
    Value cWarpSize = rewriter.create<arith::ConstantIndexOp>(loc, kWarpSize);
    Value reductionDim = rewriter.create<tensor::DimOp>(loc, input, c1);
    OpFoldResult zeroAttr = rewriter.getIndexAttr(0);
    OpFoldResult oneAttr = rewriter.getIndexAttr(1);
    auto threadDist = rewriter.getStringAttr(kThreadDistributionLabel);

    auto getResult = [](Operation* op) { return op->getResult(0); };

    // Create warp-sized partial reduction result tensor.
    Type elType = outType.getElementType();
    Value partial = rewriter.create<tensor::EmptyOp>(loc, kWarpSize, elType);
    Value outPoint =
        rewriter.create<gml_st::TileOp>(loc, zeroAttr, oneAttr, oneAttr);
    Value outElement =
        rewriter.create<gml_st::MaterializeOp>(loc, elType, output, outPoint);
    partial =
        getResult(rewriter.create<linalg::FillOp>(loc, outElement, partial));

    // Create gml_st.parallel finalizing the partial result.
    partial = getResult(rewriter.create<gml_st::ParallelOp>(
        loc, partial.getType(), c0, cWarpSize, c1, threadDist,
        [&](OpBuilder& builder, Location loc, ValueRange ivs) {
          Value laneIdx = ivs.front();
          Value partPoint = builder.create<gml_st::TileOp>(
              loc, OpFoldResult(laneIdx), oneAttr, oneAttr);
          Value initVal =
              builder.create<gml_st::MaterializeOp>(loc, partial, partPoint);
          // Create gml_st.for sequentially reducing parts of the row.
          auto forOp = builder.create<gml_st::ForOp>(
              loc, outType, laneIdx, reductionDim, cWarpSize, initVal,
              [&](OpBuilder& builder, Location loc, ValueRange ivs,
                  ValueRange outputs) {
                Value colIdx = ivs.front();
                Value partElement = outputs.front();
                using OFRs = ArrayRef<OpFoldResult>;
                Value inPoint = builder.create<gml_st::TileOp>(
                    loc, OFRs{rewriter.getIndexAttr(0), colIdx},
                    OFRs{oneAttr, oneAttr}, OFRs{oneAttr, oneAttr});
                Value inElement =
                    builder.create<gml_st::MaterializeOp>(loc, input, inPoint);
                // Clone linalg.generic op reducing two 1-element tensors.
                Operation* clonedOp = builder.clone(*genericOp.getOperation());
                clonedOp->setOperands({inElement, partElement});
                gml_st::setTransformationAttr(builder, clonedOp);
                builder.create<gml_st::SetYieldOp>(loc, clonedOp->getResult(0),
                                                   partElement, outPoint);
              });
          builder.create<gml_st::SetYieldOp>(loc, forOp.getResults(), partial,
                                             partPoint);
        }));

    // Change existing linalg.generic to warp-reduce the partial result.
    partial = rewriter.create<tensor::ExpandShapeOp>(
        loc, outType.clone({1, 32}), partial, ReassociationIndices{0, 1});
    rewriter.updateRootInPlace(genericOp, [&] {
      genericOp->setOperand(0, partial);
      gml_st::setTransformationAttr(rewriter, genericOp);
    });

    return success();
  }
};

struct TilingGPUWarpPass
    : public ::impl::TilingGPUWarpPassBase<TilingGPUWarpPass> {
  void getDependentDialects(DialectRegistry& registry) const final {
    ::impl::TilingGPUWarpPassBase<TilingGPUWarpPass>::getDependentDialects(
        registry);
    registerGmlStTilingInterfaceExternalModels(registry);
  }

  void runOnOperation() override {
    MLIRContext* ctx = &getContext();

    // Populate tiling patterns
    RewritePatternSet patterns(ctx);
    patterns.add<TilingCwisePattern, TilingReductionPattern>(ctx);

    // Populate fusion patterns.
    auto fuseGreedilyFilterFn = [](Operation* op) {
      auto materializeOp = llvm::dyn_cast<MaterializeOp>(op);
      Operation* source = materializeOp.getSource().getDefiningOp();

      // Do not fuse wap-level reductions.
      if (isSimpleReduction(source) && isWarpLevelOp(source)) return failure();

      return success();
    };
    populateFusionPatterns(ctx, fuseGreedilyFilterFn, &patterns);

    func::FuncOp func = getOperation();
    if (failed(applyPatternsAndFoldGreedily(func, std::move(patterns)))) {
      return signalPassFailure();
    }

    // Clean up by removing temporary attributes.
    func.walk([](Operation* op) { removeTransformationAttr(op); });
  }
};

}  // namespace

std::unique_ptr<OperationPass<func::FuncOp>> createTilingGPUWarpPass() {
  return std::make_unique<TilingGPUWarpPass>();
}

}  // namespace gml_st
}  // namespace mlir
<commit_msg>[GML] Extract constants in warp-level tiling<commit_after>/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>
#include <utility>

#include "llvm/ADT/SmallVector.h"
#include "mlir-hlo/Dialect/gml_st/IR/gml_st_ops.h"
#include "mlir-hlo/Dialect/gml_st/transforms/fusion.h"
#include "mlir-hlo/Dialect/gml_st/transforms/linalg_utils.h"
#include "mlir-hlo/Dialect/gml_st/transforms/passes.h"
#include "mlir-hlo/Dialect/gml_st/transforms/tiling_interface_impl.h"
#include "mlir-hlo/Dialect/gml_st/transforms/transforms.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"

#define GEN_PASS_DEF_TILINGGPUWARPPASS
#include "mlir-hlo/Dialect/gml_st/transforms/passes.h.inc"

namespace mlir {
namespace gml_st {

namespace {

constexpr const char* kWarpDistributionLabel = "warp";
constexpr const char* kThreadDistributionLabel = "thread";
constexpr int64_t kWarpSize = 32;

bool isWarpLevelOp(Operation* op) {
  if (!op) return false;
  auto parentPloop = op->getParentOfType<ParallelOp>();
  return parentPloop && parentPloop.getDistributionType() &&
         *parentPloop.getDistributionType() == kWarpDistributionLabel;
}

struct TilingCwisePattern : OpRewritePattern<linalg::GenericOp> {
  using OpRewritePattern<linalg::GenericOp>::OpRewritePattern;

  LogicalResult matchAndRewrite(linalg::GenericOp genericOp,
                                PatternRewriter& rewriter) const override {
    if (hasTransformationAttr(genericOp)) {
      return rewriter.notifyMatchFailure(genericOp, "already transformed");
    }

    // Match only cwise `linalg.generic` ops on the shape 1x?.
    auto genericOpTy =
        genericOp.getResultTypes().front().dyn_cast<RankedTensorType>();
    if (!isCwiseGenericOp(genericOp) || !genericOpTy ||
        genericOpTy.getRank() != 2 || genericOpTy.getDimSize(0) != 1) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "not a cwise op on tensor<1x?>");
    }

    // Only tile root ops on the warp level.
    if (!isWarpLevelOp(genericOp) || !genericOp->hasOneUse() ||
        !llvm::isa<SetYieldOp>(*genericOp->getUsers().begin())) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "not on warp level root op");
    }

    // Constants and attributes.
    Location loc = genericOp.getLoc();
    Value c0 = rewriter.create<arith::ConstantIndexOp>(loc, 0);
    Value c1 = rewriter.create<arith::ConstantIndexOp>(loc, 1);
    Value cWarpSize = rewriter.create<arith::ConstantIndexOp>(loc, kWarpSize);
    Value cWarpSizeMinusOne =
        rewriter.create<arith::ConstantIndexOp>(loc, kWarpSize - 1);
    Attribute zeroAttr = rewriter.getIndexAttr(0);
    Attribute oneAttr = rewriter.getIndexAttr(1);
    Attribute warpSizeAttr = rewriter.getIndexAttr(kWarpSize);
    StringAttr threadDistrLabel =
        rewriter.getStringAttr(kThreadDistributionLabel);

    // Create `gml_st.parallel` loop to distribute among lanes.
    Value init = genericOp.getOutputs().front();
    Value genericOpResult = genericOp.getResults().front();
    Value dimSize =
        rewriter.createOrFold<tensor::DimOp>(loc, genericOpResult, c1);
    Value dimSizePlusWarpSizeMinusOne =
        rewriter.createOrFold<arith::AddIOp>(loc, dimSize, cWarpSizeMinusOne);
    auto ploop = rewriter.create<gml_st::ParallelOp>(
        loc, genericOpTy, c0, cWarpSize, c1, threadDistrLabel,
        [&](OpBuilder& b, Location loc, ValueRange ivs) {
          // Compute the lane tile with a stride of `warpSize`. This tile
          // defines the subset of the result that is produced by the lane.
          // The `laneId` defines the initial offset into the tensor. The
          // remaining length to be addressed by the lane is
          //     `dimSize` - `laneId`.
          // With a stride of `warpSize`, every lane addresses a total of
          //     ceil((`dimSize` - `laneId`) / `cWarpSize`)
          //     = (`dimSize` + `cWarpSize` - 1 - `laneId`) / `cWarpSize`
          // elements.
          Value laneId = ivs.front();
          Value laneTileSize = b.create<arith::DivUIOp>(
              loc,
              b.create<arith::SubIOp>(loc, dimSizePlusWarpSizeMinusOne, laneId),
              cWarpSize);
          Value laneTile = b.createOrFold<gml_st::TileOp>(
              loc, SmallVector<OpFoldResult>{zeroAttr, laneId},
              SmallVector<OpFoldResult>{oneAttr, laneTileSize},
              SmallVector<OpFoldResult>{oneAttr, warpSizeAttr});

          // Create `gml_st.for` loop to iterate over the lane's tile.
          Type elemTy = genericOpTy.getElementType();
          auto sloopTy =
              RankedTensorType::get({1, ShapedType::kDynamicSize}, elemTy);
          Value laneInit = b.create<gml_st::MaterializeOp>(loc, init, laneTile);
          auto sloop = b.create<gml_st::ForOp>(
              loc, sloopTy, c0, laneTileSize, c1, laneInit,
              [&](OpBuilder& b, Location loc, ValueRange ivs, ValueRange aggr) {
                // Create the iteration tile. This specifies the scalar subset
                // in the warp-level operands.
                Value i = ivs.front();
                Value iterTileOffset =
                    b.create<arith::MulIOp>(loc, i, cWarpSize);
                Value iterTile = b.create<gml_st::TileOp>(
                    loc, SmallVector<OpFoldResult>{zeroAttr, iterTileOffset},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr});

                // Materialize scalar subsets per operand.
                SmallVector<Value> iterOperands =
                    llvm::to_vector(llvm::map_range(
                        genericOp.getInputs(), [&](Value arg) -> Value {
                          return b.create<gml_st::MaterializeOp>(loc, elemTy,
                                                                 arg, iterTile);
                        }));

                // Create scalar computation from `linalg.generic` body by (i)
                // mapping its block arguments to the newly materialized
                // scalar operands, and (ii) cloning the body.
                BlockAndValueMapping bvm;
                for (const auto& [blockArg, iterOperand] : llvm::zip(
                         genericOp.getBlock()->getArguments(), iterOperands)) {
                  bvm.map(blockArg, iterOperand);
                }
                for (auto& innerop :
                     genericOp.getBody()->without_terminator()) {
                  rewriter.clone(innerop, bvm);
                }

                // Yield iteration result.
                Value iterResult = bvm.lookup(genericOp.getBody()
                                                  ->getTerminator()
                                                  ->getOperands()
                                                  .front());
                Value iterTileInLaneTile = b.create<gml_st::TileOp>(
                    loc, SmallVector<OpFoldResult>{zeroAttr, i},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr},
                    SmallVector<OpFoldResult>{oneAttr, oneAttr});
                b.create<gml_st::SetYieldOp>(loc, iterResult, aggr,
                                             iterTileInLaneTile);
              });
          b.create<gml_st::SetYieldOp>(loc, sloop.getResults().front(), init,
                                       laneTile);
        });

    rewriter.replaceOp(genericOp, ploop.getResults());
    return success();
  }
};

struct TilingReductionPattern : OpRewritePattern<linalg::GenericOp> {
  using OpRewritePattern<linalg::GenericOp>::OpRewritePattern;

  LogicalResult matchAndRewrite(linalg::GenericOp genericOp,
                                PatternRewriter& rewriter) const override {
    if (gml_st::hasTransformationAttr(genericOp)) return failure();

    // Match only reductions on the warp level.
    if (!isWarpLevelOp(genericOp)) return failure();

    // Match only if it's a linalg.generic tensor<1x?xf32> -> tensor<1xf32> with
    // iterator_types = ["parallel", "reduction"].
    auto itTypes = llvm::to_vector(
        genericOp.getIteratorTypes().getAsValueRange<StringAttr>());
    if (itTypes.size() != 2 || itTypes[0] != getParallelIteratorTypeName() ||
        itTypes[1] != getReductionIteratorTypeName()) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "Expected ['parallel', 'reduction']");
    }
    if (genericOp.getNumInputs() != 1 || genericOp.getNumOutputs() != 1) {
      return rewriter.notifyMatchFailure(genericOp,
                                         "Expected single input and output");
    }

    auto input = genericOp.getInputs().front();
    auto output = genericOp.getOutputs().front();
    auto outType = output.getType().dyn_cast<TensorType>();

    Location loc = genericOp->getLoc();
    Value c0 = rewriter.create<arith::ConstantIndexOp>(loc, 0);
    Value c1 = rewriter.create<arith::ConstantIndexOp>(loc, 1);
    Value cWarpSize = rewriter.create<arith::ConstantIndexOp>(loc, kWarpSize);
    Value reductionDim = rewriter.create<tensor::DimOp>(loc, input, c1);
    OpFoldResult zeroAttr = rewriter.getIndexAttr(0);
    OpFoldResult oneAttr = rewriter.getIndexAttr(1);
    auto threadDistrLabel = rewriter.getStringAttr(kThreadDistributionLabel);

    auto getResult = [](Operation* op) { return op->getResult(0); };

    // Create warp-sized partial reduction result tensor.
    Type elType = outType.getElementType();
    Value partial = rewriter.create<tensor::EmptyOp>(loc, kWarpSize, elType);
    Value outPoint =
        rewriter.create<gml_st::TileOp>(loc, zeroAttr, oneAttr, oneAttr);
    Value outElement =
        rewriter.create<gml_st::MaterializeOp>(loc, elType, output, outPoint);
    partial =
        getResult(rewriter.create<linalg::FillOp>(loc, outElement, partial));

    // Create gml_st.parallel finalizing the partial result.
    partial = getResult(rewriter.create<gml_st::ParallelOp>(
        loc, partial.getType(), c0, cWarpSize, c1, threadDistrLabel,
        [&](OpBuilder& builder, Location loc, ValueRange ivs) {
          Value laneIdx = ivs.front();
          Value partPoint = builder.create<gml_st::TileOp>(
              loc, OpFoldResult(laneIdx), oneAttr, oneAttr);
          Value initVal =
              builder.create<gml_st::MaterializeOp>(loc, partial, partPoint);
          // Create gml_st.for sequentially reducing parts of the row.
          auto forOp = builder.create<gml_st::ForOp>(
              loc, outType, laneIdx, reductionDim, cWarpSize, initVal,
              [&](OpBuilder& builder, Location loc, ValueRange ivs,
                  ValueRange outputs) {
                Value colIdx = ivs.front();
                Value partElement = outputs.front();
                using OFRs = ArrayRef<OpFoldResult>;
                Value inPoint = builder.create<gml_st::TileOp>(
                    loc, OFRs{rewriter.getIndexAttr(0), colIdx},
                    OFRs{oneAttr, oneAttr}, OFRs{oneAttr, oneAttr});
                Value inElement =
                    builder.create<gml_st::MaterializeOp>(loc, input, inPoint);
                // Clone linalg.generic op reducing two 1-element tensors.
                Operation* clonedOp = builder.clone(*genericOp.getOperation());
                clonedOp->setOperands({inElement, partElement});
                gml_st::setTransformationAttr(builder, clonedOp);
                builder.create<gml_st::SetYieldOp>(loc, clonedOp->getResult(0),
                                                   partElement, outPoint);
              });
          builder.create<gml_st::SetYieldOp>(loc, forOp.getResults(), partial,
                                             partPoint);
        }));

    // Change existing linalg.generic to warp-reduce the partial result.
    partial = rewriter.create<tensor::ExpandShapeOp>(
        loc, outType.clone({1, kWarpSize}), partial,
        ReassociationIndices{0, 1});
    rewriter.updateRootInPlace(genericOp, [&] {
      genericOp->setOperand(0, partial);
      gml_st::setTransformationAttr(rewriter, genericOp);
    });

    return success();
  }
};

struct TilingGPUWarpPass
    : public ::impl::TilingGPUWarpPassBase<TilingGPUWarpPass> {
  void getDependentDialects(DialectRegistry& registry) const final {
    ::impl::TilingGPUWarpPassBase<TilingGPUWarpPass>::getDependentDialects(
        registry);
    registerGmlStTilingInterfaceExternalModels(registry);
  }

  void runOnOperation() override {
    MLIRContext* ctx = &getContext();

    // Populate tiling patterns
    RewritePatternSet patterns(ctx);
    patterns.add<TilingCwisePattern, TilingReductionPattern>(ctx);

    // Populate fusion patterns.
    auto fuseGreedilyFilterFn = [](Operation* op) {
      auto materializeOp = llvm::dyn_cast<MaterializeOp>(op);
      Operation* source = materializeOp.getSource().getDefiningOp();

      // Do not fuse wap-level reductions.
      if (isSimpleReduction(source) && isWarpLevelOp(source)) return failure();

      return success();
    };
    populateFusionPatterns(ctx, fuseGreedilyFilterFn, &patterns);

    func::FuncOp func = getOperation();
    if (failed(applyPatternsAndFoldGreedily(func, std::move(patterns)))) {
      return signalPassFailure();
    }

    // Clean up by removing temporary attributes.
    func.walk([](Operation* op) { removeTransformationAttr(op); });
  }
};

}  // namespace

std::unique_ptr<OperationPass<func::FuncOp>> createTilingGPUWarpPass() {
  return std::make_unique<TilingGPUWarpPass>();
}

}  // namespace gml_st
}  // namespace mlir<|endoftext|>


<commit_before>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at the top level of the distribution and at
// http://projectchrono.org/license-chrono.txt.
//
// =============================================================================
// Authors: Radu Serban
// =============================================================================
//
// Generic chase camera.  Such a camera tracks a s[pecified point on a specified
// body. It supports three different modes:
// - Chase:  camera attached to a flexible cantilever behind the body.
// - Follow: camera attached to a flexible beam articulated on the body
// - Track:  camera is fixed and tracks the body
//
// TODO:
// - relax current assumption that the body forward direction is in the negative
//   X direction.
// - add 'Inside' mode (driver POV attached to body)
//
// =============================================================================

#include "utils/ChChaseCamera.h"

namespace chrono {
namespace utils {

// -----------------------------------------------------------------------------
// Static variables
// -----------------------------------------------------------------------------

const double ChChaseCamera::m_maxTrackDist2 = 100 * 100;
const std::string ChChaseCamera::m_stateNames[] = { "Chase", "Follow", "Track" };

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChChaseCamera::ChChaseCamera(const ChSharedBodyPtr chassis)
: m_chassis(chassis),
  m_mult(1),
  m_maxMult(10),
  m_minMult(0.5f),
  m_horizGain(4.0f),
  m_vertGain(2.0f),
  m_state(Chase)
{
  Initialize(ChVector<>(0, 0, 0), 5, 1);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Initialize(const ChVector<>& ptOnChassis,
                               double            chaseDist,
                               double            chaseHeight)
{
  m_ptOnChassis = ptOnChassis;
  m_dist = chaseDist;
  m_height = chaseHeight;
  m_angle = 0;

  ChVector<> localOffset(chaseDist, 0, chaseHeight);
  m_loc = m_chassis->GetCoord().TransformLocalToParent(localOffset + ptOnChassis);
  m_lastLoc = m_loc;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::SetState(State s)
{
  m_lastLoc = m_loc;
  m_state = s;
  if (m_state == Chase)
    m_angle = 0;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Zoom(int val)
{
  if (val == 0 || m_state == Track)
    return;

  if (val < 0 && m_mult > m_minMult)
    m_mult /= 1.01;
  else if (val > 0 && m_mult < m_maxMult)
    m_mult *= 1.01;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Turn(int val)
{
  if (val == 0 || m_state != Chase)
    return;

  if (val < 0 && m_angle > -0.75 * CH_C_PI)
    m_angle -= CH_C_PI / 100;
  else if (val > 0 && m_angle < 0.75 * CH_C_PI)
    m_angle += CH_C_PI / 100;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Update(double step)
{
  // Perform a simple Euler integration step (derivative controller).
  m_loc += step * calcDeriv(m_loc);

  if (m_state != Track)
    return;

  // If in 'Track' mode, force a state change if too far from tracked body.
  double dist2 = (m_chassis->GetPos() - m_lastLoc).Length2();
  if (dist2 > m_maxTrackDist2)
    SetState(Chase);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChVector<> ChChaseCamera::calcDeriv(const ChVector<>& loc)
{
  // Calculate the desired camera location, based on the current state of the
  // chassis.
  ChVector<> targetLoc = GetTargetPos();
  ChVector<> uC2T;
  ChVector<> desCamLoc;

  if (m_state == Follow)
    uC2T = targetLoc - m_loc;
  else {
    ChQuaternion<> rot = Q_from_AngAxis(m_angle, ChVector<>(0, 0, 1));
    uC2T = rot.Rotate(-m_chassis->GetA()->Get_A_Xaxis());
  }

  uC2T.z = 0;
  uC2T.Normalize();

  desCamLoc = targetLoc - m_mult * m_dist * uC2T;
  desCamLoc.z = targetLoc.z + m_mult * m_height;

  // Calculate the derivative vector (RHS of filter ODEs).
  ChVector<> deriv;

  deriv.x = m_horizGain * (desCamLoc.x - m_loc.x);
  deriv.y = m_horizGain * (desCamLoc.y - m_loc.y);
  deriv.z = m_vertGain  * (desCamLoc.z - m_loc.z);

  return deriv;
}

}  // end namespace utils
}  // end namespace chrono
<commit_msg>Allow the chase camera to rotate completely (i.e. between -180 and 180 degrees)<commit_after>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at the top level of the distribution and at
// http://projectchrono.org/license-chrono.txt.
//
// =============================================================================
// Authors: Radu Serban
// =============================================================================
//
// Generic chase camera.  Such a camera tracks a s[pecified point on a specified
// body. It supports three different modes:
// - Chase:  camera attached to a flexible cantilever behind the body.
// - Follow: camera attached to a flexible beam articulated on the body
// - Track:  camera is fixed and tracks the body
//
// TODO:
// - relax current assumption that the body forward direction is in the negative
//   X direction.
// - add 'Inside' mode (driver POV attached to body)
//
// =============================================================================

#include "utils/ChChaseCamera.h"

namespace chrono {
namespace utils {

// -----------------------------------------------------------------------------
// Static variables
// -----------------------------------------------------------------------------

const double ChChaseCamera::m_maxTrackDist2 = 100 * 100;
const std::string ChChaseCamera::m_stateNames[] = { "Chase", "Follow", "Track" };

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChChaseCamera::ChChaseCamera(const ChSharedBodyPtr chassis)
: m_chassis(chassis),
  m_mult(1),
  m_maxMult(10),
  m_minMult(0.5f),
  m_horizGain(4.0f),
  m_vertGain(2.0f),
  m_state(Chase)
{
  Initialize(ChVector<>(0, 0, 0), 5, 1);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Initialize(const ChVector<>& ptOnChassis,
                               double            chaseDist,
                               double            chaseHeight)
{
  m_ptOnChassis = ptOnChassis;
  m_dist = chaseDist;
  m_height = chaseHeight;
  m_angle = 0;

  ChVector<> localOffset(chaseDist, 0, chaseHeight);
  m_loc = m_chassis->GetCoord().TransformLocalToParent(localOffset + ptOnChassis);
  m_lastLoc = m_loc;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::SetState(State s)
{
  m_lastLoc = m_loc;
  m_state = s;
  if (m_state == Chase)
    m_angle = 0;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Zoom(int val)
{
  if (val == 0 || m_state == Track)
    return;

  if (val < 0 && m_mult > m_minMult)
    m_mult /= 1.01;
  else if (val > 0 && m_mult < m_maxMult)
    m_mult *= 1.01;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Turn(int val)
{
  if (val == 0 || m_state != Chase)
    return;

  if (val < 0 && m_angle > -CH_C_PI)
    m_angle -= CH_C_PI / 100;
  else if (val > 0 && m_angle < CH_C_PI)
    m_angle += CH_C_PI / 100;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChChaseCamera::Update(double step)
{
  // Perform a simple Euler integration step (derivative controller).
  m_loc += step * calcDeriv(m_loc);

  if (m_state != Track)
    return;

  // If in 'Track' mode, force a state change if too far from tracked body.
  double dist2 = (m_chassis->GetPos() - m_lastLoc).Length2();
  if (dist2 > m_maxTrackDist2)
    SetState(Chase);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChVector<> ChChaseCamera::calcDeriv(const ChVector<>& loc)
{
  // Calculate the desired camera location, based on the current state of the
  // chassis.
  ChVector<> targetLoc = GetTargetPos();
  ChVector<> uC2T;
  ChVector<> desCamLoc;

  if (m_state == Follow)
    uC2T = targetLoc - m_loc;
  else {
    ChQuaternion<> rot = Q_from_AngAxis(m_angle, ChVector<>(0, 0, 1));
    uC2T = rot.Rotate(-m_chassis->GetA()->Get_A_Xaxis());
  }

  uC2T.z = 0;
  uC2T.Normalize();

  desCamLoc = targetLoc - m_mult * m_dist * uC2T;
  desCamLoc.z = targetLoc.z + m_mult * m_height;

  // Calculate the derivative vector (RHS of filter ODEs).
  ChVector<> deriv;

  deriv.x = m_horizGain * (desCamLoc.x - m_loc.x);
  deriv.y = m_horizGain * (desCamLoc.y - m_loc.y);
  deriv.z = m_vertGain  * (desCamLoc.z - m_loc.z);

  return deriv;
}

}  // end namespace utils
}  // end namespace chrono
<|endoftext|>


<commit_before>/*======================================================================

  This file is part of the elastix software.

  Copyright (c) University Medical Center Utrecht. All rights reserved.
  See src/CopyrightElastix.txt or http://elastix.isi.uu.nl/legal.php 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 notices for more information.

======================================================================*/
#include "itkBSplineDerivativeKernelFunction.h"
#include "itkBSplineDerivativeKernelFunction2.h"
#include "itkKernelFunction.h"

#include <ctime>
#include <iomanip>

//-------------------------------------------------------------------------------------

int main( int argc, char *argv[] )
{
  /** Some basic type definitions. */
  std::vector<unsigned int> splineOrders;
  splineOrders.push_back( 1 );
  splineOrders.push_back( 2 );
  splineOrders.push_back( 3 );

  /** The number of calls to Evaluate(). This number gives reasonably
   * fast test results in Release mode.
   */
  unsigned int N = static_cast<unsigned int>( 1e7 );
  const double maxAllowedDistance = 1e-5; // the allowable distance

  /** Other typedefs. */
  typedef itk::KernelFunction                       BaseKernelType;
  typedef itk::BSplineDerivativeKernelFunction<1>   KernelType_ITK_1;
  typedef itk::BSplineDerivativeKernelFunction2<1>  KernelType_elx_1;
  typedef itk::BSplineDerivativeKernelFunction<2>   KernelType_ITK_2;
  typedef itk::BSplineDerivativeKernelFunction2<2>  KernelType_elx_2;
  typedef itk::BSplineDerivativeKernelFunction<3>   KernelType_ITK_3;
  typedef itk::BSplineDerivativeKernelFunction2<3>  KernelType_elx_3;

  /** Create the evaluation points. */
  const unsigned int size_u = 15;
  std::vector<double> u( size_u );
  u[ 0 ] = -2.5;
  u[ 1 ] = -2.0;
  u[ 2 ] = -1.9;
  u[ 3 ] = -1.5;
  u[ 4 ] = -1.0;
  u[ 5 ] = -0.8;
  u[ 6 ] = -0.5;
  u[ 7 ] = -0.1;
  u[ 8 ] =  0.0;
  for ( unsigned int i = ( size_u + 3 ) / 2; i < size_u; ++i )
  {
    u[ i ] = -u[ -i + size_u + 1 ];
  }

  /** For all spline orders. */
  for ( unsigned int so = 0; so < splineOrders.size(); so++ )
  {
    std::cerr << "Evaluating spline order " << splineOrders[ so ] << std::endl;

    /** Create the kernel. */
    BaseKernelType::Pointer kernel_ITK, kernel_elx;
    if ( splineOrders[ so ] == 1 )
    {
      kernel_ITK = KernelType_ITK_1::New();
      kernel_elx = KernelType_elx_1::New();
    }
    else if ( splineOrders[ so ] == 2 )
    {
      kernel_ITK = KernelType_ITK_2::New();
      kernel_elx = KernelType_elx_2::New();
    }
    else if ( splineOrders[ so ] == 3 )
    {
      kernel_ITK = KernelType_ITK_3::New();
      kernel_elx = KernelType_elx_3::New();
    }
    else
    {
      std::cerr << "ERROR: spline order " << splineOrders[ so ]
        << " not defined." << std::endl;
      return 1;
    }

    /** Time the ITK implementation. */
    clock_t startClock = clock();
    for ( unsigned int i = 0; i < N; ++i )
    {
      for ( unsigned int j = 0; j < size_u; j++ )
      {
        kernel_ITK->Evaluate( u[ j ] );
      }
    }
    clock_t endClock = clock();
    clock_t clockDiff = endClock - startClock;
    std::cerr << "The elapsed time for ITK implementation is: "
      << clockDiff << " ms" << std::endl;

    /** Time the elx implementation. */
    startClock = clock();
    for ( unsigned int i = 0; i < N; ++i )
    {
      for ( unsigned int j = 0; j < size_u; j++ )
      {
        kernel_elx->Evaluate( u[ j ] );
      }
    }
    endClock = clock();
    clockDiff = endClock - startClock;
    std::cerr << "The elapsed time for the elastix implementation is: "
      << clockDiff << " ms"  << std::endl;

    /** Compare the results. */
    for ( unsigned int i = 0; i < size_u; ++i )
    {
      double diff = kernel_ITK->Evaluate( u[ i ] ) - kernel_elx->Evaluate( u[ i ] );
      if ( diff > maxAllowedDistance )
      {
        std::cerr << "ERROR: our implementation differs from ITK." << std::endl;
        return 1;
      }
    }
    std::cerr << "The results are good.\n" << std::endl;

  } // end for all spline orders

  /** Return a value. */
  return 0;

} // end main
<commit_msg>MS:<commit_after>/*======================================================================

  This file is part of the elastix software.

  Copyright (c) University Medical Center Utrecht. All rights reserved.
  See src/CopyrightElastix.txt or http://elastix.isi.uu.nl/legal.php 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 notices for more information.

======================================================================*/
#include "itkBSplineDerivativeKernelFunction.h"
#include "itkBSplineDerivativeKernelFunction2.h"
#include "itkKernelFunction.h"

#include <ctime>
#include <iomanip>

//-------------------------------------------------------------------------------------

int main( int argc, char *argv[] )
{
  /** Some basic type definitions. */
  std::vector<unsigned int> splineOrders;
  splineOrders.push_back( 1 );
  splineOrders.push_back( 2 );
  splineOrders.push_back( 3 );

  /** The number of calls to Evaluate(). This number gives reasonably
   * fast test results in Release mode.
   * Increase it for real time testing.
   */
  unsigned int N = static_cast<unsigned int>( 1e7 );
  const double maxAllowedDistance = 1e-5; // the allowable distance

  /** Other typedefs. */
  typedef itk::KernelFunction                       BaseKernelType;
  typedef itk::BSplineDerivativeKernelFunction<1>   KernelType_ITK_1;
  typedef itk::BSplineDerivativeKernelFunction2<1>  KernelType_elx_1;
  typedef itk::BSplineDerivativeKernelFunction<2>   KernelType_ITK_2;
  typedef itk::BSplineDerivativeKernelFunction2<2>  KernelType_elx_2;
  typedef itk::BSplineDerivativeKernelFunction<3>   KernelType_ITK_3;
  typedef itk::BSplineDerivativeKernelFunction2<3>  KernelType_elx_3;

  /** Create the evaluation points. */
  //const unsigned int size_u = 17;
  std::vector<double> u;
  u.push_back( -2.5 );
  u.push_back( -2.0 );
  u.push_back( -1.9 );
  u.push_back( -1.5 );
  u.push_back( -1.0 );
  u.push_back( -0.8 );
  u.push_back( -0.5 );
  u.push_back( -0.1 );
  u.push_back(  0.0 );
  for ( int i = static_cast<int>( u.size() ) - 2; i > -1; --i )
  {
    u.push_back( -u[ i ] );
  }

  /** For all spline orders. */
  for ( unsigned int so = 0; so < splineOrders.size(); so++ )
  {
    std::cerr << "Evaluating spline order " << splineOrders[ so ] << std::endl;

    /** Create the kernel. */
    BaseKernelType::Pointer kernel_ITK, kernel_elx;
    if ( splineOrders[ so ] == 1 )
    {
      kernel_ITK = KernelType_ITK_1::New();
      kernel_elx = KernelType_elx_1::New();
    }
    else if ( splineOrders[ so ] == 2 )
    {
      kernel_ITK = KernelType_ITK_2::New();
      kernel_elx = KernelType_elx_2::New();
    }
    else if ( splineOrders[ so ] == 3 )
    {
      kernel_ITK = KernelType_ITK_3::New();
      kernel_elx = KernelType_elx_3::New();
    }
    else
    {
      std::cerr << "ERROR: spline order " << splineOrders[ so ]
        << " not defined." << std::endl;
      return 1;
    }

    /** Print header. */
    std::cerr << "eval at:";
    for ( unsigned int j = 0; j < u.size(); j++ )
    {
      std::cerr << " " << u[ j ];
    }
    std::cerr << " average" << std::endl;

    /** Time the ITK implementation. */
    std::cerr << "ITK new:";
    clock_t startClock = clock();
    for ( unsigned int j = 0; j < u.size(); j++ )
    {
      clock_t startClockRegion = clock();
      for ( unsigned int i = 0; i < N; ++i )
      {
        kernel_ITK->Evaluate( u[ j ] );
      }
      std::cerr << " " << clock() - startClockRegion;
    }
    clock_t endClock = clock();
    clock_t clockDiff = endClock - startClock;
    std::cerr << " " << clockDiff << " ms" << std::endl;

    /** Time the elx implementation. */
    std::cerr << "elastix:";
    startClock = clock();
    for ( unsigned int j = 0; j < u.size(); j++ )
    {
      clock_t startClockRegion = clock();
      for ( unsigned int i = 0; i < N; ++i )
      {
        kernel_elx->Evaluate( u[ j ] );
      }
      std::cerr << " " << clock() - startClockRegion;
    }
    endClock = clock();
    clockDiff = endClock - startClock;
    std::cerr << " " << clockDiff << " ms" << std::endl;

    /** Compare the results. */
    for ( unsigned int i = 0; i < u.size(); ++i )
    {
      double diff = kernel_ITK->Evaluate( u[ i ] ) - kernel_elx->Evaluate( u[ i ] );
      if ( diff > maxAllowedDistance )
      {
        std::cerr << "ERROR: our implementation differs from ITK." << std::endl;
        return 1;
      }
    }
    std::cerr << "The results are good.\n" << std::endl;

  } // end for all spline orders

  /** Return a value. */
  return 0;

} // end main
<|endoftext|>


<commit_before>#include "precompiled.hpp"
#include "util/MetaTypeConverters.hpp"
#include "util/TypeInfo.hpp"

#include <QString>
#include <QtTest>
#include <QList>
#include <QCoreApplication>
#include "ui/Uniforms.hpp"
#include "util/Logging.hpp"

using namespace glm;
using balls::Uniforms;
using balls::UniformCollection;
using balls::UniformInfo;

class TestUniformsTest : public QObject {
  Q_OBJECT

private slots:
  void initTestCase();

  void elapsedTimeIncreases();
  void elapsedTimeIsUInt();
  void filtersEvents();
  void mouseCoordinates();
  void mouseCoordinates_data();
  void receiveUniforms();
  void receiveUniforms_data();
};

void TestUniformsTest::initTestCase() {
  QLoggingCategory::setFilterRules("uniform*=false");
  balls::registerMetaTypeConverters();
  balls::util::types::init();

  QVERIFY(balls::util::types::info.size() > 0);
  QVERIFY(QMetaType::type("vec2") != QMetaType::UnknownType);
}

void TestUniformsTest::receiveUniforms_data() {
  QTest::addColumn<UniformCollection>("beforeCompile");
  QTest::addColumn<UniformCollection>("afterCompile");

  QTest::newRow("empty -> empty") << UniformCollection {} << UniformCollection {};

}

void TestUniformsTest::receiveUniforms() {
  QFETCH(UniformCollection, beforeCompile);
  QFETCH(UniformCollection, afterCompile);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  uniforms.receiveUniforms(beforeCompile);

  QCOMPARE(uniforms.uniformInfo(), beforeCompile);

  uniforms.receiveUniforms(afterCompile);

}

void TestUniformsTest::mouseCoordinates_data() {
  QTest::addColumn<QList<QPoint>>("points");

  using QPointList = QList<QPoint>;
  QTest::newRow("basic") << QPointList {{1, 1}, {42, 43}, {100, 99}};
  QTest::newRow("not moving") << QPointList {{0, 0}, {0, 0}, {0, 0}};
  QTest::newRow("negative") << QPointList {{ -1, -43}, { -119, -32}, { -55, -22}};
  QTest::newRow("change signs") << QPointList {{0, 0}, {34, -66}, { -22, 78}, { -99, -101}, {0, 0}, {1, 0}};
}

void TestUniformsTest::mouseCoordinates() {
  QFETCH(QList<QPoint>, points);
  Q_ASSERT(points.length() >= 2);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  for (const QPoint& p : points) {
    QMouseEvent event(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton,
                      Qt::NoModifier);
    QCoreApplication::sendEvent(&object, &event);
    // WORKAROUND: QTest::mouseMove doesn't actually fire a QMouseEvent (it just sets the mouse cursor)
  }

  ivec2 mousePos = uniforms.property("mousePos").value<ivec2>();
  ivec2 lastMousePos = uniforms.property("lastMousePos").value<ivec2>();

  const QPoint& pos = points.last();
  const QPoint& lastPos = points[points.length() - 2];

  QCOMPARE(mousePos.x, pos.x());
  QCOMPARE(mousePos.y, pos.y());

  QCOMPARE(lastMousePos.x, lastPos.x());
  QCOMPARE(lastMousePos.y, lastPos.y());
}

void TestUniformsTest::filtersEvents() {
  QObject object;
  Uniforms uniforms;
  QSize size {100, 100};
  QResizeEvent resize {size, {2, 2}};

  object.installEventFilter(&uniforms);
  QCoreApplication::sendEvent(&object, &resize);

  ivec2 canvasSize = uniforms.property("canvasSize").value<ivec2>();

  QCOMPARE(canvasSize.x, size.width());
  QCOMPARE(canvasSize.y, size.height());
}

void TestUniformsTest::elapsedTimeIsUInt() {
  Uniforms uniforms;

  QVariant a = uniforms.property("elapsedTime");
  QCOMPARE(a.type(), QVariant::UInt);
}

void TestUniformsTest::elapsedTimeIncreases() {
  Uniforms uniforms;

  QTest::qSleep(50);
  uint a = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint b = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint c = uniforms.property("elapsedTime").toUInt();

  QVERIFY(a != 0);
  QVERIFY(b != 0);
  QVERIFY(c != 0);
  QVERIFY(b > a);
  QVERIFY(c > b);
}

QTEST_MAIN(TestUniformsTest)

#include "tst_TestUniformsTest.moc"
<commit_msg>Simplify the receiveUniforms test and add data<commit_after>#include "precompiled.hpp"
#include "util/MetaTypeConverters.hpp"
#include "util/TypeInfo.hpp"

#include <QString>
#include <QtTest>
#include <QList>
#include <QCoreApplication>
#include "ui/Uniforms.hpp"
#include "util/Logging.hpp"

using namespace glm;
using balls::Uniforms;
using balls::UniformCollection;
using balls::UniformInfo;

class TestUniformsTest : public QObject {
  Q_OBJECT

private slots:
  void initTestCase();

  void elapsedTimeIncreases();
  void elapsedTimeIsUInt();
  void filtersEvents();
  void mouseCoordinates();
  void mouseCoordinates_data();
  void receiveUniforms();
  void receiveUniforms_data();
};

void TestUniformsTest::initTestCase() {
  QLoggingCategory::setFilterRules("uniform*=false");
  balls::registerMetaTypeConverters();
  balls::util::types::init();

  QVERIFY(balls::util::types::info.size() > 0);
  QVERIFY(QMetaType::type("vec2") != QMetaType::UnknownType);
}

void TestUniformsTest::receiveUniforms_data() {
  QTest::addColumn<UniformCollection>("beforeCompile");
  QTest::addColumn<UniformCollection>("result");

  QTest::newRow("empty -> empty")
      << UniformCollection {}
      << UniformCollection {};

  QTest::newRow("empty -> one matrix")
  << UniformCollection {
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  };

  QTest::newRow("one matrix -> empty")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
  };

  QTest::newRow("matrix -> vector")
  << UniformCollection {
    { "matrix", GL_FLOAT_MAT4, 1 }
  }
  << UniformCollection {
    { "matrix", GL_FLOAT_VEC4, 1 }
  };

  QTest::newRow("rearrange")
  << UniformCollection {
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
    { "available", GL_BOOL, 1},
  }
  << UniformCollection {
    { "available", GL_BOOL, 1},
    { "matrix", GL_DOUBLE_MAT4, 1},
    { "scale", GL_FLOAT, 1},
  };

  QTest::newRow("rearrange and change types")
  << UniformCollection {
    { "mvp", GL_FLOAT_MAT3, 1 },
    { "color", GL_FLOAT_VEC4, 1 },
    { "repetitions", GL_INT, 1 },
    { "seed", GL_INT_VEC2, 1 },
  }
  << UniformCollection {
    { "color", GL_DOUBLE_VEC4, 1 },
    { "seed", GL_UNSIGNED_INT, 1 },
    { "mvp", GL_FLOAT_MAT3x4, 1 },
    { "repetitions", GL_UNSIGNED_INT, 1 },
  };

  QTest::newRow("unsupported types")
  << UniformCollection {
    { "coords", GL_FLOAT_VEC2, 1 },
    { "threshold", GL_FLOAT, 1 },
  }
  << UniformCollection {
    { "threshold", GL_FLOAT, 1 },
    { "texture", GL_SAMPLER_2D, 1 },
    { "coords", GL_FLOAT_VEC2, 1 },
    { "palette", GL_INT_SAMPLER_1D, 1 },
  };
}

void TestUniformsTest::receiveUniforms() {
  QFETCH(UniformCollection, beforeCompile);
  QFETCH(UniformCollection, result);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  uniforms.receiveUniforms(beforeCompile);

  QCOMPARE(uniforms.uniformInfo(), beforeCompile);

  uniforms.receiveUniforms(result);

  QCOMPARE(uniforms.uniformInfo(), result);
}

void TestUniformsTest::mouseCoordinates_data() {
  QTest::addColumn<QList<QPoint>>("points");

  using QPointList = QList<QPoint>;
  QTest::newRow("basic") << QPointList {{1, 1}, {42, 43}, {100, 99}};
  QTest::newRow("not moving") << QPointList {{0, 0}, {0, 0}, {0, 0}};
  QTest::newRow("negative") << QPointList {{ -1, -43}, { -119, -32}, { -55, -22}};
  QTest::newRow("change signs") << QPointList {{0, 0}, {34, -66}, { -22, 78}, { -99, -101}, {0, 0}, {1, 0}};
}

void TestUniformsTest::mouseCoordinates() {
  QFETCH(QList<QPoint>, points);
  Q_ASSERT(points.length() >= 2);

  QObject object;
  Uniforms uniforms;

  object.installEventFilter(&uniforms);

  for (const QPoint& p : points) {
    QMouseEvent event(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton,
                      Qt::NoModifier);
    QCoreApplication::sendEvent(&object, &event);
    // WORKAROUND: QTest::mouseMove doesn't actually fire a QMouseEvent (it just sets the mouse cursor)
  }

  ivec2 mousePos = uniforms.property("mousePos").value<ivec2>();
  ivec2 lastMousePos = uniforms.property("lastMousePos").value<ivec2>();

  const QPoint& pos = points.last();
  const QPoint& lastPos = points[points.length() - 2];

  QCOMPARE(mousePos.x, pos.x());
  QCOMPARE(mousePos.y, pos.y());

  QCOMPARE(lastMousePos.x, lastPos.x());
  QCOMPARE(lastMousePos.y, lastPos.y());
}

void TestUniformsTest::filtersEvents() {
  QObject object;
  Uniforms uniforms;
  QSize size {100, 100};
  QResizeEvent resize {size, {2, 2}};

  object.installEventFilter(&uniforms);
  QCoreApplication::sendEvent(&object, &resize);

  ivec2 canvasSize = uniforms.property("canvasSize").value<ivec2>();

  QCOMPARE(canvasSize.x, size.width());
  QCOMPARE(canvasSize.y, size.height());
}

void TestUniformsTest::elapsedTimeIsUInt() {
  Uniforms uniforms;

  QVariant a = uniforms.property("elapsedTime");
  QCOMPARE(a.type(), QVariant::UInt);
}

void TestUniformsTest::elapsedTimeIncreases() {
  Uniforms uniforms;

  QTest::qSleep(50);
  uint a = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint b = uniforms.property("elapsedTime").toUInt();
  QTest::qSleep(50);
  uint c = uniforms.property("elapsedTime").toUInt();

  QVERIFY(a != 0);
  QVERIFY(b != 0);
  QVERIFY(c != 0);
  QVERIFY(b > a);
  QVERIFY(c > b);
}

QTEST_MAIN(TestUniformsTest)

#include "tst_TestUniformsTest.moc"
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/test/layout_browsertest.h"

namespace content {

class IndexedDBLayoutTest : public InProcessBrowserLayoutTest {
 public:
  IndexedDBLayoutTest() : InProcessBrowserLayoutTest(
      base::FilePath(),
      base::FilePath().AppendASCII("storage").AppendASCII("indexeddb")) {
  }

  void RunLayoutTests(const char* file_names[]) {
    for (size_t i = 0; file_names[i]; i++)
      RunLayoutTest(file_names[i]);
  }
};

namespace {

static const char* kBasicTests[] = {
  "basics.html",
  "basics-shared-workers.html",
  // Failing on Precise bot (crbug.com/145592).
  // "basics-workers.html",
  "database-basics.html",
  "factory-basics.html",
  "index-basics.html",
  "objectstore-basics.html",
  NULL
};

static const char* kComplexTests[] = {
  "prefetch-bugfix-108071.html",
  // Flaky: http://crbug.com/123685
  // "pending-version-change-stuck-works-with-terminate.html",
  "pending-version-change-on-exit.html",
  NULL
};

static const char* kIndexTests[] = {
  "deleteIndex.html",
  // Flaky: http://crbug.com/123685
  // "index-basics-workers.html",
  "index-count.html",
  "index-cursor.html",
  "index-get-key-argument-required.html",
  "index-multientry.html",
  "index-population.html",
  "index-unique.html",
  NULL
};

static const char* kKeyTests[] = {
  "key-generator.html",
  "keypath-basics.html",
  "keypath-edges.html",
  "keypath-fetch-key.html",
  "keyrange.html",
  "keyrange-required-arguments.html",
  "key-sort-order-across-types.html",
  "key-sort-order-date.html",
  // Flaky: http://crbug.com/165671
  //"key-type-array.html",
  "key-type-infinity.html",
  "invalid-keys.html",
  NULL
};

static const char* kTransactionTests[] = {
  "transaction-abort.html",
  "transaction-complete-with-js-recursion-cross-frame.html",
  "transaction-complete-with-js-recursion.html",
  "transaction-complete-workers.html",
  "transaction-after-close.html",
  "transaction-and-objectstore-calls.html",
  "transaction-basics.html",
  "transaction-crash-on-abort.html",
  "transaction-event-propagation.html",
  "transaction-read-only.html",
  "transaction-rollback.html",
  "transaction-storeNames-required.html",
  NULL
};

static const char* kRegressionTests[] = {
  "dont-commit-on-blocked.html",
  NULL
};

const char* kIntVersionTests1[] = {
  "intversion-abort-in-initial-upgradeneeded.html",
  "intversion-blocked.html",
  "intversion-close-between-events.html",
  "intversion-close-in-oncomplete.html",
  "intversion-close-in-upgradeneeded.html",
  "delete-in-upgradeneeded-close-in-open-success.html",
  NULL
};

const char* kIntVersionTests2[] = {
  "delete-in-upgradeneeded-close-in-versionchange.html",
  "intversion-gated-on-delete.html",
  "intversion-long-queue.html",
  "intversion-omit-parameter.html",
  "intversion-open-with-version.html",
  "intversion-upgrades.html",
  NULL
};

}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, BasicTests) {
  RunLayoutTests(kBasicTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, ComplexTests) {
  RunLayoutTests(kComplexTests);
}

// TODO(dgrogan): times out flakily. http://crbug.com/153064
// Marking FLAKY to diagnose if this is just a test duration
// issue (and we need to split it up) or something else.
IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, FLAKY_IndexTests) {
  RunLayoutTests(kIndexTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, KeyTests) {
  RunLayoutTests(kKeyTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, TransactionTests) {
  RunLayoutTests(kTransactionTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, IntVersionTests1) {
  RunLayoutTests(kIntVersionTests1);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, IntVersionTests2) {
  RunLayoutTests(kIntVersionTests2);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, RegressionTests) {
  RunLayoutTests(kRegressionTests);
}

// TODO(jsbell): Remove this when data has been gathered.
IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, FLAKY_FlakyTests) {
  RunLayoutTest("key-type-array.html");
}

}  // namespace content
<commit_msg>IndexedDB: Re-enable previously flaky tests<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/test/layout_browsertest.h"

namespace content {

class IndexedDBLayoutTest : public InProcessBrowserLayoutTest {
 public:
  IndexedDBLayoutTest() : InProcessBrowserLayoutTest(
      base::FilePath(),
      base::FilePath().AppendASCII("storage").AppendASCII("indexeddb")) {
  }

  void RunLayoutTests(const char* file_names[]) {
    for (size_t i = 0; file_names[i]; i++)
      RunLayoutTest(file_names[i]);
  }
};

namespace {

static const char* kBasicTests[] = {
  "basics.html",
  "basics-shared-workers.html",
  // Failing on Precise bot (crbug.com/145592).
  // "basics-workers.html",
  "database-basics.html",
  "factory-basics.html",
  "index-basics.html",
  "objectstore-basics.html",
  NULL
};

static const char* kComplexTests[] = {
  "prefetch-bugfix-108071.html",
  // Flaky: http://crbug.com/123685
  // "pending-version-change-stuck-works-with-terminate.html",
  "pending-version-change-on-exit.html",
  NULL
};

static const char* kIndexTests[] = {
  "deleteIndex.html",
  // Flaky: http://crbug.com/123685
  // "index-basics-workers.html",
  "index-count.html",
  "index-cursor.html",
  "index-get-key-argument-required.html",
  "index-multientry.html",
  "index-population.html",
  "index-unique.html",
  NULL
};

static const char* kKeyTests[] = {
  "key-generator.html",
  "keypath-basics.html",
  "keypath-edges.html",
  "keypath-fetch-key.html",
  "keyrange.html",
  "keyrange-required-arguments.html",
  "key-sort-order-across-types.html",
  "key-sort-order-date.html",
  "key-type-array.html",
  "key-type-infinity.html",
  "invalid-keys.html",
  NULL
};

static const char* kTransactionTests[] = {
  "transaction-abort.html",
  "transaction-complete-with-js-recursion-cross-frame.html",
  "transaction-complete-with-js-recursion.html",
  "transaction-complete-workers.html",
  "transaction-after-close.html",
  "transaction-and-objectstore-calls.html",
  "transaction-basics.html",
  "transaction-crash-on-abort.html",
  "transaction-event-propagation.html",
  "transaction-read-only.html",
  "transaction-rollback.html",
  "transaction-storeNames-required.html",
  NULL
};

static const char* kRegressionTests[] = {
  "dont-commit-on-blocked.html",
  NULL
};

const char* kIntVersionTests1[] = {
  "intversion-abort-in-initial-upgradeneeded.html",
  "intversion-blocked.html",
  "intversion-close-between-events.html",
  "intversion-close-in-oncomplete.html",
  "intversion-close-in-upgradeneeded.html",
  "delete-in-upgradeneeded-close-in-open-success.html",
  NULL
};

const char* kIntVersionTests2[] = {
  "delete-in-upgradeneeded-close-in-versionchange.html",
  "intversion-gated-on-delete.html",
  "intversion-long-queue.html",
  "intversion-omit-parameter.html",
  "intversion-open-with-version.html",
  "intversion-upgrades.html",
  NULL
};

}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, BasicTests) {
  RunLayoutTests(kBasicTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, ComplexTests) {
  RunLayoutTests(kComplexTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, IndexTests) {
  RunLayoutTests(kIndexTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, KeyTests) {
  RunLayoutTests(kKeyTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, TransactionTests) {
  RunLayoutTests(kTransactionTests);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, IntVersionTests1) {
  RunLayoutTests(kIntVersionTests1);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, IntVersionTests2) {
  RunLayoutTests(kIntVersionTests2);
}

IN_PROC_BROWSER_TEST_F(IndexedDBLayoutTest, RegressionTests) {
  RunLayoutTests(kRegressionTests);
}

}  // namespace content
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <concepts>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <seastar/core/print.hh>

#include "bytes.hh"

enum class mutable_view { no, yes, };

/// Fragmented buffer
///
/// Concept `FragmentedBuffer` is satisfied by any class that is a range of
/// fragments and provides a method `size_bytes()` which returns the total
/// size of the buffer. The interfaces accepting `FragmentedBuffer` will attempt
/// to avoid unnecessary linearisation.
template<typename T>
concept FragmentRange = requires (T range) {
    typename T::fragment_type;
    requires std::is_same_v<typename T::fragment_type, bytes_view>
        || std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
    { *range.begin() } -> std::convertible_to<const typename T::fragment_type&>;
    { *range.end() } -> std::convertible_to<const typename T::fragment_type&>;
    { range.size_bytes() } -> std::convertible_to<size_t>;
    { range.empty() } -> std::same_as<bool>; // returns true iff size_bytes() == 0.
};

template<typename T, typename = void>
struct is_fragment_range : std::false_type { };

template<typename T>
struct is_fragment_range<T, std::void_t<typename T::fragment_type>> : std::true_type { };

template<typename T>
static constexpr bool is_fragment_range_v = is_fragment_range<T>::value;

/// A non-mutable view of a FragmentRange
///
/// Provide a trivially copyable and movable, non-mutable view on a
/// fragment range. This allows uniform ownership semantics across
/// multi-fragment ranges and the single fragment and empty fragment
/// adaptors below, i.e. it allows treating all fragment ranges
/// uniformly as views.
template <typename T>
requires FragmentRange<T>
class fragment_range_view {
    const T* _range;
public:
    using fragment_type = typename T::fragment_type;
    using iterator = typename T::const_iterator;
    using const_iterator = typename T::const_iterator;

public:
    explicit fragment_range_view(const T& range) : _range(&range) { }

    const_iterator begin() const { return _range->begin(); }
    const_iterator end() const { return _range->end(); }

    size_t size_bytes() const { return _range->size_bytes(); }
    bool empty() const { return _range->empty(); }
};

/// Single-element fragment range
///
/// This is a helper that allows converting a bytes_view into a FragmentRange.
template<mutable_view is_mutable>
class single_fragment_range {
public:
    using fragment_type = std::conditional_t<is_mutable == mutable_view::no,
                                             bytes_view, bytes_mutable_view>;
private:
    fragment_type _view;
public:
    using iterator = const fragment_type*;
    using const_iterator = const fragment_type*;

    explicit single_fragment_range(fragment_type f) : _view { f } { }

    const_iterator begin() const { return &_view; }
    const_iterator end() const { return &_view + 1; }

    size_t size_bytes() const { return _view.size(); }
    bool empty() const { return _view.empty(); }
};

single_fragment_range(bytes_view) -> single_fragment_range<mutable_view::no>;
single_fragment_range(bytes_mutable_view) -> single_fragment_range<mutable_view::yes>;

/// Empty fragment range.
struct empty_fragment_range {
    using fragment_type = bytes_view;
    using iterator = bytes_view*;
    using const_iterator = bytes_view*;

    iterator begin() const { return nullptr; }
    iterator end() const { return nullptr; }

    size_t size_bytes() const { return 0; }
    bool empty() const { return true; }
};

static_assert(FragmentRange<empty_fragment_range>);
static_assert(FragmentRange<single_fragment_range<mutable_view::no>>);
static_assert(FragmentRange<single_fragment_range<mutable_view::yes>>);

template<typename FragmentedBuffer>
requires FragmentRange<FragmentedBuffer>
bytes linearized(const FragmentedBuffer& buffer)
{
    bytes b(bytes::initialized_later(), buffer.size_bytes());
    auto dst = b.begin();
    for (bytes_view fragment : buffer) {
        dst = boost::copy(fragment, dst);
    }
    return b;
}

template<typename FragmentedBuffer, typename Function>
requires FragmentRange<FragmentedBuffer> && requires (Function fn, bytes_view bv) {
    fn(bv);
}
decltype(auto) with_linearized(const FragmentedBuffer& buffer, Function&& fn)
{
    bytes b;
    bytes_view bv;
    if (__builtin_expect(!buffer.empty() && std::next(buffer.begin()) == buffer.end(), true)) {
        bv = *buffer.begin();
    } else if (!buffer.empty()) {
        b = linearized(buffer);
        bv = b;
    }
    return fn(bv);
}

template<typename T>
concept FragmentedView = requires (T view, size_t n) {
    typename T::fragment_type;
    requires std::is_same_v<typename T::fragment_type, bytes_view>
            || std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
    // No preconditions.
    { view.current_fragment() } -> std::convertible_to<const typename T::fragment_type&>;
    // No preconditions.
    { view.empty() } -> std::same_as<bool>;
    // No preconditions.
    { view.size_bytes() } -> std::convertible_to<size_t>;
    // Precondition: n <= size_bytes()
    { view.prefix(n) } -> std::same_as<T>;
    // Precondition: n <= size_bytes()
    view.remove_prefix(n);
    // Precondition: size_bytes() > 0
    view.remove_current();
};

template<typename T>
concept FragmentedMutableView = requires (T view) {
    requires FragmentedView<T>;
    requires std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
};

template<FragmentedView View>
struct fragment_range {
    using fragment_type = typename View::fragment_type;
    View view;
    class fragment_iterator {
        using iterator_category = std::input_iterator_tag;
        using value_type = typename View::fragment_type;
        using difference_type = std::ptrdiff_t;
        using pointer = const value_type*;
        using reference = const value_type&;
        View _view;
        value_type _current;
    public:
        fragment_iterator() : _view(value_type()) {}
        fragment_iterator(const View& v) : _view(v) {
            _current = _view.current_fragment();
        }
        fragment_iterator& operator++() {
            _view.remove_current();
            _current = _view.current_fragment();
            return *this;
        }
        fragment_iterator operator++(int) {
            fragment_iterator i(*this);
            ++(*this);
            return i;
        }
        reference operator*() const { return _current; }
        pointer operator->() const { return &_current; }
        bool operator==(const fragment_iterator& i) const { return _view.size_bytes() == i._view.size_bytes(); }
    };
    using iterator = fragment_iterator;
    fragment_range(const View& v) : view(v) {}
    fragment_iterator begin() const { return fragment_iterator(view); }
    fragment_iterator end() const { return fragment_iterator(); }
    size_t size_bytes() const { return view.size_bytes(); }
    bool empty() const { return view.empty(); }
};

template<FragmentedView View>
requires (!FragmentRange<View>)
bytes linearized(View v)
{
    bytes b(bytes::initialized_later(), v.size_bytes());
    auto out = b.begin();
    while (v.size_bytes()) {
        out = std::copy(v.current_fragment().begin(), v.current_fragment().end(), out);
        v.remove_current();
    }
    return b;
}

template<FragmentedView View, typename Function>
requires (!FragmentRange<View>) && std::invocable<Function, bytes_view>
decltype(auto) with_linearized(const View& v, Function&& fn)
{
    if (v.size_bytes() == v.current_fragment().size()) [[likely]] {
        return fn(v.current_fragment());
    } else {
        return fn(linearized(v));
    }
}

class single_fragmented_view {
    bytes_view _view;
public:
    using fragment_type = bytes_view;
    explicit single_fragmented_view(bytes_view bv) : _view(bv) {}
    size_t size_bytes() const { return _view.size(); }
    bool empty() const { return _view.empty(); }
    void remove_prefix(size_t n) { _view.remove_prefix(n); }
    void remove_current() { _view = bytes_view(); }
    bytes_view current_fragment() const { return _view; }
    single_fragmented_view prefix(size_t n) { return single_fragmented_view(_view.substr(0, n)); }
};
static_assert(FragmentedView<single_fragmented_view>);

template<FragmentedView View, typename Function>
requires std::invocable<Function, View> && std::invocable<Function, single_fragmented_view>
decltype(auto) with_simplified(const View& v, Function&& fn)
{
    if (v.size_bytes() == v.current_fragment().size()) [[likely]] {
        return fn(single_fragmented_view(v.current_fragment()));
    } else {
        return fn(v);
    }
}

template<FragmentedView V1, FragmentedView V2>
int compare_unsigned(V1 v1, V2 v2) {
    while (!v1.empty() && !v2.empty()) {
        size_t n = std::min(v1.current_fragment().size(), v2.current_fragment().size());
        if (int d = memcmp(v1.current_fragment().data(), v2.current_fragment().data(), n)) {
            return d;
        }
        v1.remove_prefix(n);
        v2.remove_prefix(n);
    }
    return v1.size_bytes() - v2.size_bytes();
}

template<FragmentedMutableView Dest, FragmentedView Src>
void write_fragmented(Dest& dest, Src src) {
    if (dest.size_bytes() < src.size_bytes()) [[unlikely]] {
        throw std::out_of_range(format("tried to copy a buffer of size {} to a buffer of smaller size {}", src.size_bytes(), dest.size_bytes()));
    }
    while (!src.empty()) {
        size_t n = std::min(dest.current_fragment().size(), src.current_fragment().size());
        memcpy(dest.current_fragment().data(), src.current_fragment().data(), n);
        dest.remove_prefix(n);
        src.remove_prefix(n);
    }
}
<commit_msg>utils: fragment_range: add single_fragmented_mutable_view<commit_after>/*
 * Copyright (C) 2018 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <concepts>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <seastar/core/print.hh>

#include "bytes.hh"

enum class mutable_view { no, yes, };

/// Fragmented buffer
///
/// Concept `FragmentedBuffer` is satisfied by any class that is a range of
/// fragments and provides a method `size_bytes()` which returns the total
/// size of the buffer. The interfaces accepting `FragmentedBuffer` will attempt
/// to avoid unnecessary linearisation.
template<typename T>
concept FragmentRange = requires (T range) {
    typename T::fragment_type;
    requires std::is_same_v<typename T::fragment_type, bytes_view>
        || std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
    { *range.begin() } -> std::convertible_to<const typename T::fragment_type&>;
    { *range.end() } -> std::convertible_to<const typename T::fragment_type&>;
    { range.size_bytes() } -> std::convertible_to<size_t>;
    { range.empty() } -> std::same_as<bool>; // returns true iff size_bytes() == 0.
};

template<typename T, typename = void>
struct is_fragment_range : std::false_type { };

template<typename T>
struct is_fragment_range<T, std::void_t<typename T::fragment_type>> : std::true_type { };

template<typename T>
static constexpr bool is_fragment_range_v = is_fragment_range<T>::value;

/// A non-mutable view of a FragmentRange
///
/// Provide a trivially copyable and movable, non-mutable view on a
/// fragment range. This allows uniform ownership semantics across
/// multi-fragment ranges and the single fragment and empty fragment
/// adaptors below, i.e. it allows treating all fragment ranges
/// uniformly as views.
template <typename T>
requires FragmentRange<T>
class fragment_range_view {
    const T* _range;
public:
    using fragment_type = typename T::fragment_type;
    using iterator = typename T::const_iterator;
    using const_iterator = typename T::const_iterator;

public:
    explicit fragment_range_view(const T& range) : _range(&range) { }

    const_iterator begin() const { return _range->begin(); }
    const_iterator end() const { return _range->end(); }

    size_t size_bytes() const { return _range->size_bytes(); }
    bool empty() const { return _range->empty(); }
};

/// Single-element fragment range
///
/// This is a helper that allows converting a bytes_view into a FragmentRange.
template<mutable_view is_mutable>
class single_fragment_range {
public:
    using fragment_type = std::conditional_t<is_mutable == mutable_view::no,
                                             bytes_view, bytes_mutable_view>;
private:
    fragment_type _view;
public:
    using iterator = const fragment_type*;
    using const_iterator = const fragment_type*;

    explicit single_fragment_range(fragment_type f) : _view { f } { }

    const_iterator begin() const { return &_view; }
    const_iterator end() const { return &_view + 1; }

    size_t size_bytes() const { return _view.size(); }
    bool empty() const { return _view.empty(); }
};

single_fragment_range(bytes_view) -> single_fragment_range<mutable_view::no>;
single_fragment_range(bytes_mutable_view) -> single_fragment_range<mutable_view::yes>;

/// Empty fragment range.
struct empty_fragment_range {
    using fragment_type = bytes_view;
    using iterator = bytes_view*;
    using const_iterator = bytes_view*;

    iterator begin() const { return nullptr; }
    iterator end() const { return nullptr; }

    size_t size_bytes() const { return 0; }
    bool empty() const { return true; }
};

static_assert(FragmentRange<empty_fragment_range>);
static_assert(FragmentRange<single_fragment_range<mutable_view::no>>);
static_assert(FragmentRange<single_fragment_range<mutable_view::yes>>);

template<typename FragmentedBuffer>
requires FragmentRange<FragmentedBuffer>
bytes linearized(const FragmentedBuffer& buffer)
{
    bytes b(bytes::initialized_later(), buffer.size_bytes());
    auto dst = b.begin();
    for (bytes_view fragment : buffer) {
        dst = boost::copy(fragment, dst);
    }
    return b;
}

template<typename FragmentedBuffer, typename Function>
requires FragmentRange<FragmentedBuffer> && requires (Function fn, bytes_view bv) {
    fn(bv);
}
decltype(auto) with_linearized(const FragmentedBuffer& buffer, Function&& fn)
{
    bytes b;
    bytes_view bv;
    if (__builtin_expect(!buffer.empty() && std::next(buffer.begin()) == buffer.end(), true)) {
        bv = *buffer.begin();
    } else if (!buffer.empty()) {
        b = linearized(buffer);
        bv = b;
    }
    return fn(bv);
}

template<typename T>
concept FragmentedView = requires (T view, size_t n) {
    typename T::fragment_type;
    requires std::is_same_v<typename T::fragment_type, bytes_view>
            || std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
    // No preconditions.
    { view.current_fragment() } -> std::convertible_to<const typename T::fragment_type&>;
    // No preconditions.
    { view.empty() } -> std::same_as<bool>;
    // No preconditions.
    { view.size_bytes() } -> std::convertible_to<size_t>;
    // Precondition: n <= size_bytes()
    { view.prefix(n) } -> std::same_as<T>;
    // Precondition: n <= size_bytes()
    view.remove_prefix(n);
    // Precondition: size_bytes() > 0
    view.remove_current();
};

template<typename T>
concept FragmentedMutableView = requires (T view) {
    requires FragmentedView<T>;
    requires std::is_same_v<typename T::fragment_type, bytes_mutable_view>;
};

template<FragmentedView View>
struct fragment_range {
    using fragment_type = typename View::fragment_type;
    View view;
    class fragment_iterator {
        using iterator_category = std::input_iterator_tag;
        using value_type = typename View::fragment_type;
        using difference_type = std::ptrdiff_t;
        using pointer = const value_type*;
        using reference = const value_type&;
        View _view;
        value_type _current;
    public:
        fragment_iterator() : _view(value_type()) {}
        fragment_iterator(const View& v) : _view(v) {
            _current = _view.current_fragment();
        }
        fragment_iterator& operator++() {
            _view.remove_current();
            _current = _view.current_fragment();
            return *this;
        }
        fragment_iterator operator++(int) {
            fragment_iterator i(*this);
            ++(*this);
            return i;
        }
        reference operator*() const { return _current; }
        pointer operator->() const { return &_current; }
        bool operator==(const fragment_iterator& i) const { return _view.size_bytes() == i._view.size_bytes(); }
    };
    using iterator = fragment_iterator;
    fragment_range(const View& v) : view(v) {}
    fragment_iterator begin() const { return fragment_iterator(view); }
    fragment_iterator end() const { return fragment_iterator(); }
    size_t size_bytes() const { return view.size_bytes(); }
    bool empty() const { return view.empty(); }
};

template<FragmentedView View>
requires (!FragmentRange<View>)
bytes linearized(View v)
{
    bytes b(bytes::initialized_later(), v.size_bytes());
    auto out = b.begin();
    while (v.size_bytes()) {
        out = std::copy(v.current_fragment().begin(), v.current_fragment().end(), out);
        v.remove_current();
    }
    return b;
}

template<FragmentedView View, typename Function>
requires (!FragmentRange<View>) && std::invocable<Function, bytes_view>
decltype(auto) with_linearized(const View& v, Function&& fn)
{
    if (v.size_bytes() == v.current_fragment().size()) [[likely]] {
        return fn(v.current_fragment());
    } else {
        return fn(linearized(v));
    }
}

template <mutable_view is_mutable>
class basic_single_fragmented_view {
public:
    using fragment_type = std::conditional_t<is_mutable == mutable_view::yes, bytes_mutable_view, bytes_view>;
private:
    fragment_type _view;
public:
    explicit basic_single_fragmented_view(fragment_type bv) : _view(bv) {}
    size_t size_bytes() const { return _view.size(); }
    bool empty() const { return _view.empty(); }
    void remove_prefix(size_t n) { _view.remove_prefix(n); }
    void remove_current() { _view = fragment_type(); }
    fragment_type current_fragment() const { return _view; }
    basic_single_fragmented_view prefix(size_t n) { return basic_single_fragmented_view(_view.substr(0, n)); }
};
using single_fragmented_view = basic_single_fragmented_view<mutable_view::no>;
using single_fragmented_mutable_view = basic_single_fragmented_view<mutable_view::yes>;
static_assert(FragmentedView<single_fragmented_view>);
static_assert(FragmentedMutableView<single_fragmented_mutable_view>);
static_assert(FragmentRange<fragment_range<single_fragmented_view>>);
static_assert(FragmentRange<fragment_range<single_fragmented_mutable_view>>);

template<FragmentedView View, typename Function>
requires std::invocable<Function, View> && std::invocable<Function, single_fragmented_view>
decltype(auto) with_simplified(const View& v, Function&& fn)
{
    if (v.size_bytes() == v.current_fragment().size()) [[likely]] {
        return fn(single_fragmented_view(v.current_fragment()));
    } else {
        return fn(v);
    }
}

template<FragmentedView V1, FragmentedView V2>
int compare_unsigned(V1 v1, V2 v2) {
    while (!v1.empty() && !v2.empty()) {
        size_t n = std::min(v1.current_fragment().size(), v2.current_fragment().size());
        if (int d = memcmp(v1.current_fragment().data(), v2.current_fragment().data(), n)) {
            return d;
        }
        v1.remove_prefix(n);
        v2.remove_prefix(n);
    }
    return v1.size_bytes() - v2.size_bytes();
}

template<FragmentedMutableView Dest, FragmentedView Src>
void write_fragmented(Dest& dest, Src src) {
    if (dest.size_bytes() < src.size_bytes()) [[unlikely]] {
        throw std::out_of_range(format("tried to copy a buffer of size {} to a buffer of smaller size {}", src.size_bytes(), dest.size_bytes()));
    }
    while (!src.empty()) {
        size_t n = std::min(dest.current_fragment().size(), src.current_fragment().size());
        memcpy(dest.current_fragment().data(), src.current_fragment().data(), n);
        dest.remove_prefix(n);
        src.remove_prefix(n);
    }
}
<|endoftext|>


<commit_before>#include <iostream>
#include "cMapper.h"

cMapper* cMapper::thisPointer = NULL;
bool cMapper::instanceFlag = false;

cMapper* cMapper::getInstance() {
    if (!instanceFlag)
    {
        thisPointer = new cMapper();
        instanceFlag = true;
        return thisPointer;
    }
    else
    {
        return thisPointer;
    }
}

void cMapper::add(gameEntity *ent) {
    entityContainer.push_back(ent);
    std::cout << "Added gameEntity: " << ent->name() << " to container!\n";
}

void cMapper::showEntities() {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();
    std::cout << "mapper contains: ";
    for (;iter < entityContainer.end();iter++)
        std::cout << (*iter)->name() << ", ";
    std::cout << "\n";
}

void cMapper::update(long frametime) {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();

    for (;iter < entityContainer.end();iter++)
        (*iter)->update(frametime);
}

gameEntity* cMapper::getTarget() {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();

    for (;iter < entityContainer.end();iter++)
        if ((*iter)->name() == "Enemy")
            return (*iter);
}

void cMapper::deleteInstance(gameEntity *instance) {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();
    for (;iter < entityContainer.end();iter++)
        if ((*iter) == instance)
            entityContainer.erase(iter);
}
<commit_msg>Return value fix in cMapper getTarget.<commit_after>#include <iostream>
#include "cMapper.h"

cMapper* cMapper::thisPointer = NULL;
bool cMapper::instanceFlag = false;

cMapper* cMapper::getInstance() {
    if (!instanceFlag)
    {
        thisPointer = new cMapper();
        instanceFlag = true;
        return thisPointer;
    }
    else
    {
        return thisPointer;
    }
}

void cMapper::add(gameEntity *ent) {
    entityContainer.push_back(ent);
    std::cout << "Added gameEntity: " << ent->name() << " to container!\n";
}

void cMapper::showEntities() {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();
    std::cout << "mapper contains: ";
    for (;iter < entityContainer.end();iter++)
        std::cout << (*iter)->name() << ", ";
    std::cout << "\n";
}

void cMapper::update(long frametime) {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();

    for (;iter < entityContainer.end();iter++)
        (*iter)->update(frametime);
}

gameEntity* cMapper::getTarget() {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();

    for (;iter < entityContainer.end();iter++)
        if ((*iter)->name() == "Enemy")
            return (*iter);
    return NULL;
}

void cMapper::deleteInstance(gameEntity *instance) {
    std::vector<gameEntity*>::iterator iter = entityContainer.begin();
    for (;iter < entityContainer.end();iter++)
        if ((*iter) == instance)
            entityContainer.erase(iter);
}
<|endoftext|>