blob: 6073ec40841334a1d8074e20537a9fdba790d450 [file] [log] [blame]
/*
* Copyright 2022 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkTypes.h"
#include "include/private/SkTArray.h"
#include "src/core/SkRasterPipeline.h"
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <memory>
class SkArenaAlloc;
namespace SkSL {
namespace RP {
// A single scalar in our program consumes one slot.
using Slot = int;
constexpr Slot NA = -1;
// Scalars, vectors, and matrices can be represented as a range of slot indices.
struct SlotRange {
Slot index = 0;
int count = 0;
};
// Represents a single raster-pipeline SkSL instruction.
struct Instruction {
Instruction(SkRasterPipeline::Stage op, std::initializer_list<Slot> slots)
: fOp(op), fImmF32(0.0f), fImmI32(0) {
auto iter = slots.begin();
if (iter != slots.end()) { fSlotA = *iter++; }
if (iter != slots.end()) { fSlotB = *iter++; }
if (iter != slots.end()) { fSlotC = *iter++; }
SkASSERT(iter == slots.end());
}
Instruction(SkRasterPipeline::Stage op, std::initializer_list<Slot> slots, float f, int i)
: fOp(op), fImmF32(f), fImmI32(i) {
auto iter = slots.begin();
if (iter != slots.end()) { fSlotA = *iter++; }
if (iter != slots.end()) { fSlotB = *iter++; }
if (iter != slots.end()) { fSlotC = *iter++; }
SkASSERT(iter == slots.end());
}
SkRasterPipeline::Stage fOp;
Slot fSlotA = NA;
Slot fSlotB = NA;
Slot fSlotC = NA;
float fImmF32 = 0.0f;
int fImmI32 = 0;
};
class Program {
public:
Program(SkTArray<Instruction> instrs);
void appendStages(SkRasterPipeline* pipeline, SkArenaAlloc* alloc);
private:
void optimize();
int numValueSlots();
int numConditionMaskSlots();
SkTArray<Instruction> fInstructions;
int fNumValueSlots = 0;
int fNumConditionMaskSlots = 0;
};
class Builder {
public:
/** Finalizes and optimizes the program. */
std::unique_ptr<Program> finish();
/** Assemble a program from the Raster Pipeline instructions below. */
void init_lane_masks() {
fInstructions.push_back({SkRasterPipeline::init_lane_masks, {}});
}
void store_src_rg(SlotRange slots) {
SkASSERT(slots.count == 2);
fInstructions.push_back({SkRasterPipeline::store_src_rg, {slots.index}});
}
void store_src(SlotRange slots) {
SkASSERT(slots.count == 4);
fInstructions.push_back({SkRasterPipeline::store_src, {slots.index}});
}
void store_dst(SlotRange slots) {
SkASSERT(slots.count == 4);
fInstructions.push_back({SkRasterPipeline::store_dst, {slots.index}});
}
void load_src(SlotRange slots) {
SkASSERT(slots.count == 4);
fInstructions.push_back({SkRasterPipeline::load_src, {slots.index}});
}
void load_dst(SlotRange slots) {
SkASSERT(slots.count == 4);
fInstructions.push_back({SkRasterPipeline::load_dst, {slots.index}});
}
void immediate_f(float val) {
fInstructions.push_back({SkRasterPipeline::immediate_f, {}, val, 0});
}
void immediate_i(int32_t val) {
// SkRasterPipeline registers are floats, so it's easiest just to reuse immediate_f here.
float immF;
memcpy(&immF, &val, sizeof(float));
fInstructions.push_back({SkRasterPipeline::immediate_f, {}, immF, 0});
}
void immediate_u(uint32_t val) {
// SkRasterPipeline registers are floats, so it's easiest just to reuse immediate_f here.
float immF;
memcpy(&immF, &val, sizeof(float));
fInstructions.push_back({SkRasterPipeline::immediate_f, {}, immF, 0});
}
void load_unmasked(Slot slot) {
fInstructions.push_back({SkRasterPipeline::load_unmasked, {slot}});
}
void store_unmasked(Slot slot) {
fInstructions.push_back({SkRasterPipeline::store_unmasked, {slot}});
}
void push_condition_mask() {
// Raster pipeline uses a "store" op, and the builder manages the stack position.
fInstructions.push_back({SkRasterPipeline::store_condition_mask, {}});
}
void pop_condition_mask() {
// Raster pipeline uses a "load" op, and the builder manages the stack position.
fInstructions.push_back({SkRasterPipeline::load_condition_mask, {}});
}
private:
SkTArray<Instruction> fInstructions;
};
} // namespace RP
} // namespace SkSL