blob: d054c03c2f11a3ebcd6eecaaecdb1f612c181914 [file] [log] [blame]
// Copyright 2018 The Amber Authors.
//
// 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 "src/vkscript/command_parser.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <limits>
#include <string>
#include <utility>
#include "src/command_data.h"
#include "src/make_unique.h"
#include "src/tokenizer.h"
#include "src/vkscript/datum_type_parser.h"
namespace amber {
namespace vkscript {
namespace {
ShaderType ShaderNameToType(const std::string& name) {
if (name == "fragment")
return kShaderTypeFragment;
if (name == "compute")
return kShaderTypeCompute;
if (name == "geometry")
return kShaderTypeGeometry;
if (name == "tessellation evaluation")
return kShaderTypeTessellationEvaluation;
if (name == "tessellation control")
return kShaderTypeTessellationControl;
return kShaderTypeVertex;
}
} // namespace
CommandParser::CommandParser(Script* script,
Pipeline* pipeline,
size_t current_line,
const std::string& data)
: script_(script),
pipeline_(pipeline),
tokenizer_(MakeUnique<Tokenizer>(data)) {
tokenizer_->SetCurrentLine(current_line);
}
CommandParser::~CommandParser() = default;
std::string CommandParser::make_error(const std::string& err) {
return std::to_string(tokenizer_->GetCurrentLine()) + ": " + err;
}
Result CommandParser::ParseBoolean(const std::string& str, bool* result) {
assert(result);
std::string tmp;
tmp.resize(str.size());
std::transform(str.begin(), str.end(), tmp.begin(),
[](unsigned char c) { return std::tolower(c); });
if (tmp == "true") {
*result = true;
return {};
}
if (tmp == "false") {
*result = false;
return {};
}
return Result("Invalid value passed as a boolean string: " + str);
}
Result CommandParser::Parse() {
for (auto token = tokenizer_->NextToken(); !token->IsEOS();
token = tokenizer_->NextToken()) {
if (token->IsEOL())
continue;
if (!token->IsString()) {
return Result(make_error(
"Command not recognized. Received something other then a string: " +
token->ToOriginalString()));
}
std::string cmd_name = token->AsString();
Result r;
if (cmd_name == "draw") {
token = tokenizer_->NextToken();
if (!token->IsString())
return Result(make_error("Invalid draw command in test: " +
token->ToOriginalString()));
cmd_name = token->AsString();
if (cmd_name == "rect")
r = ProcessDrawRect();
else if (cmd_name == "arrays")
r = ProcessDrawArrays();
else
r = Result("Unknown draw command: " + cmd_name);
} else if (cmd_name == "clear") {
r = ProcessClear();
} else if (cmd_name == "ssbo") {
r = ProcessSSBO();
} else if (cmd_name == "uniform") {
r = ProcessUniform();
} else if (cmd_name == "patch") {
r = ProcessPatch();
} else if (cmd_name == "probe") {
r = ProcessProbe(false);
} else if (cmd_name == "tolerance") {
r = ProcessTolerance();
} else if (cmd_name == "relative") {
token = tokenizer_->NextToken();
if (!token->IsString() || token->AsString() != "probe")
return Result(make_error("relative must be used with probe: " +
token->ToOriginalString()));
r = ProcessProbe(true);
} else if (cmd_name == "compute") {
r = ProcessCompute();
} else if (cmd_name == "vertex" || cmd_name == "fragment" ||
cmd_name == "geometry" || cmd_name == "tessellation") {
std::string shader_name = cmd_name;
if (cmd_name == "tessellation") {
token = tokenizer_->NextToken();
if (!token->IsString() || (token->AsString() != "control" &&
token->AsString() != "evaluation")) {
return Result(
make_error("Tessellation entrypoint must have "
"<evaluation|control> in name: " +
token->ToOriginalString()));
}
shader_name += " " + token->AsString();
}
token = tokenizer_->NextToken();
if (!token->IsString() || token->AsString() != "entrypoint")
return Result(make_error("Unknown command: " + shader_name));
r = ProcessEntryPoint(shader_name);
// Pipeline Commands
} else if (cmd_name == "primitiveRestartEnable") {
r = ProcessPrimitiveRestartEnable();
} else if (cmd_name == "depthClampEnable") {
r = ProcessDepthClampEnable();
} else if (cmd_name == "rasterizerDiscardEnable") {
r = ProcessRasterizerDiscardEnable();
} else if (cmd_name == "depthBiasEnable") {
r = ProcessDepthBiasEnable();
} else if (cmd_name == "logicOpEnable") {
r = ProcessLogicOpEnable();
} else if (cmd_name == "blendEnable") {
r = ProcessBlendEnable();
} else if (cmd_name == "depthTestEnable") {
r = ProcessDepthTestEnable();
} else if (cmd_name == "depthWriteEnable") {
r = ProcessDepthWriteEnable();
} else if (cmd_name == "depthBoundsTestEnable") {
r = ProcessDepthBoundsTestEnable();
} else if (cmd_name == "stencilTestEnable") {
r = ProcessStencilTestEnable();
} else if (cmd_name == "topology") {
r = ProcessTopology();
} else if (cmd_name == "polygonMode") {
r = ProcessPolygonMode();
} else if (cmd_name == "logicOp") {
r = ProcessLogicOp();
} else if (cmd_name == "frontFace") {
r = ProcessFrontFace();
} else if (cmd_name == "cullMode") {
r = ProcessCullMode();
} else if (cmd_name == "depthBiasConstantFactor") {
r = ProcessDepthBiasConstantFactor();
} else if (cmd_name == "depthBiasClamp") {
r = ProcessDepthBiasClamp();
} else if (cmd_name == "depthBiasSlopeFactor") {
r = ProcessDepthBiasSlopeFactor();
} else if (cmd_name == "lineWidth") {
r = ProcessLineWidth();
} else if (cmd_name == "minDepthBounds") {
r = ProcessMinDepthBounds();
} else if (cmd_name == "maxDepthBounds") {
r = ProcessMaxDepthBounds();
} else if (cmd_name == "srcColorBlendFactor") {
r = ProcessSrcColorBlendFactor();
} else if (cmd_name == "dstColorBlendFactor") {
r = ProcessDstColorBlendFactor();
} else if (cmd_name == "srcAlphaBlendFactor") {
r = ProcessSrcAlphaBlendFactor();
} else if (cmd_name == "dstAlphaBlendFactor") {
r = ProcessDstAlphaBlendFactor();
} else if (cmd_name == "colorBlendOp") {
r = ProcessColorBlendOp();
} else if (cmd_name == "alphaBlendOp") {
r = ProcessAlphaBlendOp();
} else if (cmd_name == "depthCompareOp") {
r = ProcessDepthCompareOp();
} else if (cmd_name == "front.compareOp") {
r = ProcessFrontCompareOp();
} else if (cmd_name == "back.compareOp") {
r = ProcessBackCompareOp();
} else if (cmd_name == "front.failOp") {
r = ProcessFrontFailOp();
} else if (cmd_name == "front.passOp") {
r = ProcessFrontPassOp();
} else if (cmd_name == "front.depthFailOp") {
r = ProcessFrontDepthFailOp();
} else if (cmd_name == "back.failOp") {
r = ProcessBackFailOp();
} else if (cmd_name == "back.passOp") {
r = ProcessBackPassOp();
} else if (cmd_name == "back.depthFailOp") {
r = ProcessBackDepthFailOp();
} else if (cmd_name == "front.compareMask") {
r = ProcessFrontCompareMask();
} else if (cmd_name == "front.writeMask") {
r = ProcessFrontWriteMask();
} else if (cmd_name == "back.compareMask") {
r = ProcessBackCompareMask();
} else if (cmd_name == "back.writeMask") {
r = ProcessBackWriteMask();
} else if (cmd_name == "front.reference") {
r = ProcessFrontReference();
} else if (cmd_name == "back.reference") {
r = ProcessBackReference();
} else if (cmd_name == "colorWriteMask") {
r = ProcessColorWriteMask();
} else {
r = Result("Unknown command: " + cmd_name);
}
if (!r.IsSuccess())
return Result(make_error(r.Error()));
}
return {};
}
Result CommandParser::ProcessDrawRect() {
auto cmd = MakeUnique<DrawRectCommand>(pipeline_, pipeline_data_);
cmd->SetLine(tokenizer_->GetCurrentLine());
if (pipeline_->GetVertexBuffers().size() > 1) {
return Result(
"draw rect command is not supported in a pipeline with more than one "
"vertex buffer attached");
}
auto token = tokenizer_->NextToken();
while (token->IsString()) {
std::string str = token->AsString();
if (str != "ortho" && str != "patch")
return Result("Unknown parameter to draw rect: " + str);
if (str == "ortho") {
cmd->EnableOrtho();
} else {
cmd->EnablePatch();
}
token = tokenizer_->NextToken();
}
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetX(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetY(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetWidth(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetHeight(token->AsFloat());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter to draw rect command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessDrawArrays() {
auto cmd = MakeUnique<DrawArraysCommand>(pipeline_, pipeline_data_);
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
while (token->IsString()) {
std::string str = token->AsString();
if (str != "indexed" && str != "instanced") {
Topology topo = NameToTopology(token->AsString());
if (topo != Topology::kUnknown) {
cmd->SetTopology(topo);
// Advance token here so we're consistent with the non-topology case.
token = tokenizer_->NextToken();
break;
}
return Result("Unknown parameter to draw arrays: " + str);
}
if (str == "indexed") {
cmd->EnableIndexed();
} else {
cmd->EnableInstanced();
}
token = tokenizer_->NextToken();
}
if (cmd->GetTopology() == Topology::kUnknown)
return Result("Missing draw arrays topology");
if (!token->IsInteger())
return Result("Missing integer first vertex value for draw arrays: " +
token->ToOriginalString());
cmd->SetFirstVertexIndex(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("Missing integer vertex count value for draw arrays: " +
token->ToOriginalString());
cmd->SetVertexCount(token->AsUint32());
token = tokenizer_->NextToken();
if (cmd->IsInstanced()) {
if (!token->IsEOL() && !token->IsEOS()) {
if (!token->IsInteger())
return Result("Invalid instance count for draw arrays: " +
token->ToOriginalString());
cmd->SetInstanceCount(token->AsUint32());
}
token = tokenizer_->NextToken();
}
if (!token->IsEOL() && !token->IsEOS())
return Result("Extra parameter to draw arrays command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessCompute() {
auto cmd = MakeUnique<ComputeCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
// Compute can start a compute line or an entryp oint line ...
if (token->IsString() && token->AsString() == "entrypoint")
return ProcessEntryPoint("compute");
if (!token->IsInteger())
return Result("Missing integer value for compute X entry: " +
token->ToOriginalString());
cmd->SetX(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("Missing integer value for compute Y entry: " +
token->ToOriginalString());
cmd->SetY(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("Missing integer value for compute Z entry: " +
token->ToOriginalString());
cmd->SetZ(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter to compute command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessClear() {
std::unique_ptr<Command> cmd;
auto token = tokenizer_->NextToken();
std::string cmd_suffix = "";
if (token->IsString()) {
std::string str = token->AsString();
cmd_suffix = str + " ";
if (str == "depth") {
cmd = MakeUnique<ClearDepthCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
token = tokenizer_->NextToken();
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->AsClearDepth()->SetValue(token->AsFloat());
} else if (str == "stencil") {
cmd = MakeUnique<ClearStencilCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing stencil value for clear stencil command: " +
token->ToOriginalString());
if (!token->IsInteger())
return Result("Invalid stencil value for clear stencil command: " +
token->ToOriginalString());
cmd->AsClearStencil()->SetValue(token->AsUint32());
} else if (str == "color") {
cmd = MakeUnique<ClearColorCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
token = tokenizer_->NextToken();
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->AsClearColor()->SetR(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->AsClearColor()->SetG(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->AsClearColor()->SetB(token->AsFloat());
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->AsClearColor()->SetA(token->AsFloat());
} else {
return Result("Extra parameter to clear command: " +
token->ToOriginalString());
}
token = tokenizer_->NextToken();
} else {
cmd = MakeUnique<ClearCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
}
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter to clear " + cmd_suffix +
"command: " + token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ParseValues(const std::string& name,
Format* fmt,
std::vector<Value>* values) {
assert(values);
uint32_t row_index = 0;
auto token = tokenizer_->NextToken();
size_t seen = 0;
while (!token->IsEOL() && !token->IsEOS()) {
Value v;
if ((fmt->IsFloat() || fmt->IsDouble())) {
if (!token->IsInteger() && !token->IsDouble()) {
return Result(std::string("Invalid value provided to ") + name +
" command: " + token->ToOriginalString());
}
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
v.SetDoubleValue(token->AsDouble());
} else {
if (!token->IsInteger()) {
return Result(std::string("Invalid value provided to ") + name +
" command: " + token->ToOriginalString());
}
v.SetIntValue(token->AsUint64());
}
values->push_back(v);
token = tokenizer_->NextToken();
++row_index;
++seen;
}
// This could overflow, but I don't really expect us to get command files
// that big ....
size_t num_per_row = fmt->RowCount();
if (seen == 0 || (seen % num_per_row) != 0) {
return Result(std::string("Incorrect number of values provided to ") +
name + " command");
}
return {};
}
Result CommandParser::ProcessSSBO() {
auto cmd =
MakeUnique<BufferCommand>(BufferCommand::BufferType::kSSBO, pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing binding and size values for ssbo command");
if (!token->IsInteger())
return Result("Invalid binding value for ssbo command");
uint32_t val = token->AsUint32();
token = tokenizer_->NextToken();
if (token->IsString() && token->AsString() != "subdata") {
auto& str = token->AsString();
if (str.size() >= 2 && str[0] == ':') {
cmd->SetDescriptorSet(val);
auto substr = str.substr(1, str.size());
uint64_t binding_val = strtoul(substr.c_str(), nullptr, 10);
if (binding_val > std::numeric_limits<uint32_t>::max())
return Result("binding value too large in ssbo command");
cmd->SetBinding(static_cast<uint32_t>(binding_val));
} else {
return Result("Invalid value for ssbo command: " +
token->ToOriginalString());
}
token = tokenizer_->NextToken();
} else {
cmd->SetBinding(val);
}
{
// Generate an internal buffer for this binding if needed.
auto set = cmd->GetDescriptorSet();
auto binding = cmd->GetBinding();
auto* buffer = pipeline_->GetBufferForBinding(set, binding);
if (!buffer) {
auto b = MakeUnique<Buffer>(BufferType::kStorage);
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
pipeline_->AddBuffer(buffer, set, binding);
}
cmd->SetBuffer(buffer);
}
if (token->IsString() && token->AsString() == "subdata") {
cmd->SetIsSubdata();
token = tokenizer_->NextToken();
if (!token->IsString())
return Result("Invalid type for ssbo command: " +
token->ToOriginalString());
DatumTypeParser tp;
auto fmt = tp.Parse(token->AsString());
if (!fmt)
return Result("Invalid type provided: " + token->AsString());
auto* buf = cmd->GetBuffer();
if (buf->FormatIsDefault() || !buf->GetFormat())
buf->SetFormat(std::move(fmt));
else if (!buf->GetFormat()->Equal(fmt.get()))
return Result("probe ssbo format does not match buffer format");
token = tokenizer_->NextToken();
if (!token->IsInteger()) {
return Result("Invalid offset for ssbo command: " +
token->ToOriginalString());
}
if (token->AsInt32() < 0) {
return Result("offset for SSBO must be positive, got: " +
std::to_string(token->AsInt32()));
}
if ((token->AsUint32() % buf->GetFormat()->SizeInBytes()) != 0) {
return Result(
"offset for SSBO must be a multiple of the data size expected " +
std::to_string(buf->GetFormat()->SizeInBytes()));
}
cmd->SetOffset(token->AsUint32());
std::vector<Value> values;
Result r = ParseValues("ssbo", buf->GetFormat(), &values);
if (!r.IsSuccess())
return r;
buf->RecalculateMaxSizeInBytes(values, cmd->GetOffset());
cmd->SetValues(std::move(values));
} else {
if (token->IsEOL() || token->IsEOS())
return Result("Missing size value for ssbo command: " +
token->ToOriginalString());
if (!token->IsInteger())
return Result("Invalid size value for ssbo command: " +
token->ToOriginalString());
// Resize the buffer so we'll correctly create the descriptor sets.
auto* buf = cmd->GetBuffer();
buf->SetElementCount(token->AsUint32());
// Set a default format into the buffer if needed.
if (!buf->GetFormat()) {
auto fmt = MakeUnique<Format>();
fmt->SetFormatType(FormatType::kR8_SINT);
fmt->AddComponent(FormatComponentType::kR, FormatMode::kSInt, 8);
buf->SetFormat(std::move(fmt));
// This has to come after the SetFormat() call because SetFormat() resets
// the value back to false.
buf->SetFormatIsDefault(true);
}
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for ssbo command: " +
token->ToOriginalString());
}
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessUniform() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing binding and size values for uniform command: " +
token->ToOriginalString());
if (!token->IsString())
return Result("Invalid type value for uniform command: " +
token->ToOriginalString());
std::unique_ptr<BufferCommand> cmd;
bool is_ubo = false;
if (token->AsString() == "ubo") {
cmd = MakeUnique<BufferCommand>(BufferCommand::BufferType::kUniform,
pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
token = tokenizer_->NextToken();
if (!token->IsInteger()) {
return Result("Invalid binding value for uniform ubo command: " +
token->ToOriginalString());
}
uint32_t val = token->AsUint32();
token = tokenizer_->NextToken();
if (!token->IsString()) {
return Result("Invalid type value for uniform ubo command: " +
token->ToOriginalString());
}
auto& str = token->AsString();
if (str.size() >= 2 && str[0] == ':') {
cmd->SetDescriptorSet(val);
auto substr = str.substr(1, str.size());
uint64_t binding_val = strtoul(substr.c_str(), nullptr, 10);
if (binding_val > std::numeric_limits<uint32_t>::max())
return Result("binding value too large in uniform ubo command: " +
token->ToOriginalString());
cmd->SetBinding(static_cast<uint32_t>(binding_val));
token = tokenizer_->NextToken();
if (!token->IsString()) {
return Result("Invalid type value for uniform ubo command: " +
token->ToOriginalString());
}
} else {
cmd->SetBinding(val);
}
is_ubo = true;
auto set = cmd->GetDescriptorSet();
auto binding = cmd->GetBinding();
auto* buffer = pipeline_->GetBufferForBinding(set, binding);
if (!buffer) {
auto b = MakeUnique<Buffer>(BufferType::kUniform);
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
pipeline_->AddBuffer(buffer, set, binding);
}
cmd->SetBuffer(buffer);
} else {
cmd = MakeUnique<BufferCommand>(BufferCommand::BufferType::kPushConstant,
pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
// Push constants don't have descriptor set and binding values. So, we do
// not want to try to lookup the buffer or we'll accidentally get whatever
// is bound at 0:0.
auto b = MakeUnique<Buffer>(BufferType::kUniform);
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
cmd->SetBuffer(b.get());
script_->AddBuffer(std::move(b));
}
DatumTypeParser tp;
auto fmt = tp.Parse(token->AsString());
if (!fmt)
return Result("Invalid type provided: " + token->AsString());
// uniform is always std140.
if (is_ubo)
fmt->SetIsStd140();
auto* buf = cmd->GetBuffer();
if (buf->FormatIsDefault() || !buf->GetFormat())
buf->SetFormat(std::move(fmt));
else if (!buf->GetFormat()->Equal(fmt.get()))
return Result("probe ssbo format does not match buffer format");
token = tokenizer_->NextToken();
if (!token->IsInteger()) {
return Result("Invalid offset value for uniform command: " +
token->ToOriginalString());
}
if (token->AsInt32() < 0) {
return Result("offset for uniform must be positive, got: " +
std::to_string(token->AsInt32()));
}
auto buf_size = static_cast<int32_t>(buf->GetFormat()->SizeInBytes());
if (token->AsInt32() % buf_size != 0)
return Result("offset for uniform must be multiple of data size");
cmd->SetOffset(token->AsUint32());
std::vector<Value> values;
Result r = ParseValues("uniform", buf->GetFormat(), &values);
if (!r.IsSuccess())
return r;
buf->RecalculateMaxSizeInBytes(values, cmd->GetOffset());
if (cmd->IsPushConstant())
buf->SetData(values);
else
cmd->SetValues(std::move(values));
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessTolerance() {
current_tolerances_.clear();
auto token = tokenizer_->NextToken();
size_t found_tokens = 0;
while (!token->IsEOL() && !token->IsEOS() && found_tokens < 4) {
if (token->IsString() && token->AsString() == ",") {
token = tokenizer_->NextToken();
continue;
}
if (token->IsInteger() || token->IsDouble()) {
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
double value = token->AsDouble();
token = tokenizer_->NextToken();
if (token->IsString() && token->AsString() != ",") {
if (token->AsString() != "%")
return Result("Invalid value for tolerance command: " +
token->ToOriginalString());
current_tolerances_.push_back(Probe::Tolerance{true, value});
token = tokenizer_->NextToken();
} else {
current_tolerances_.push_back(Probe::Tolerance{false, value});
}
} else {
return Result("Invalid value for tolerance command: " +
token->ToOriginalString());
}
++found_tokens;
}
if (found_tokens == 0)
return Result("Missing value for tolerance command");
if (found_tokens != 1 && found_tokens != 4)
return Result("Invalid number of tolerance parameters provided");
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for tolerance command: " +
token->ToOriginalString());
return {};
}
Result CommandParser::ProcessPatch() {
auto cmd = MakeUnique<PatchParameterVerticesCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
if (!token->IsString() || token->AsString() != "parameter")
return Result("Missing parameter flag to patch command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsString() || token->AsString() != "vertices")
return Result("Missing vertices flag to patch command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("Invalid count parameter for patch parameter vertices: " +
token->ToOriginalString());
cmd->SetControlPointCount(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for patch parameter vertices command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessEntryPoint(const std::string& name) {
auto cmd = MakeUnique<EntryPointCommand>(pipeline_);
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing entrypoint name");
if (!token->IsString())
return Result("Entrypoint name must be a string: " +
token->ToOriginalString());
cmd->SetShaderType(ShaderNameToType(name));
cmd->SetEntryPointName(token->AsString());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for entrypoint command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessProbe(bool relative) {
auto token = tokenizer_->NextToken();
if (!token->IsString())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
// The SSBO syntax is different from probe or probe all so handle specially.
if (token->AsString() == "ssbo")
return ProcessProbeSSBO();
if (pipeline_->GetColorAttachments().empty())
return Result("Pipeline missing color buffers. Something went wrong.");
// VkScript has a single generated colour buffer which should always be
// available.
auto* buffer = pipeline_->GetColorAttachments()[0].buffer;
if (!buffer)
return Result("Pipeline missing color buffers, something went wrong.");
auto cmd = MakeUnique<ProbeCommand>(buffer);
cmd->SetLine(tokenizer_->GetCurrentLine());
cmd->SetTolerances(current_tolerances_);
if (relative)
cmd->SetRelative();
bool is_rect = false;
if (token->AsString() == "rect") {
is_rect = true;
cmd->SetProbeRect();
token = tokenizer_->NextToken();
if (!token->IsString())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
} else if (token->AsString() == "all") {
cmd->SetWholeWindow();
cmd->SetProbeRect();
token = tokenizer_->NextToken();
if (!token->IsString())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
}
std::string format = token->AsString();
if (format != "rgba" && format != "rgb")
return Result("Invalid format specified to probe command: " +
token->ToOriginalString());
if (format == "rgba")
cmd->SetIsRGBA();
token = tokenizer_->NextToken();
if (!cmd->IsWholeWindow()) {
bool got_rect_open_bracket = false;
if (token->IsOpenBracket()) {
got_rect_open_bracket = true;
token = tokenizer_->NextToken();
}
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetX(token->AsFloat());
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetY(token->AsFloat());
if (is_rect) {
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetWidth(token->AsFloat());
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetHeight(token->AsFloat());
}
token = tokenizer_->NextToken();
if (token->IsCloseBracket()) {
// Close bracket without an open
if (!got_rect_open_bracket)
return Result("Missing open bracket for probe command");
token = tokenizer_->NextToken();
} else if (got_rect_open_bracket) {
// An open bracket without a close bracket.
return Result("Missing close bracket for probe command");
}
}
bool got_color_open_bracket = false;
if (token->IsOpenBracket()) {
got_color_open_bracket = true;
token = tokenizer_->NextToken();
}
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetR(token->AsFloat());
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetG(token->AsFloat());
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetB(token->AsFloat());
if (format == "rgba") {
token = tokenizer_->NextToken();
if (token->IsComma())
token = tokenizer_->NextToken();
r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
cmd->SetA(token->AsFloat());
}
token = tokenizer_->NextToken();
if (token->IsCloseBracket()) {
if (!got_color_open_bracket) {
// Close without an open.
return Result("Missing open bracket for probe command");
}
token = tokenizer_->NextToken();
} else if (got_color_open_bracket) {
// Open bracket without a close.
return Result("Missing close bracket for probe command");
}
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter to probe command: " +
token->ToOriginalString());
commands_.push_back(std::move(cmd));
return {};
}
Result CommandParser::ProcessTopology() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for topology command");
if (!token->IsString())
return Result("Invalid value for topology command: " +
token->ToOriginalString());
Topology topology = Topology::kPatchList;
std::string topo = token->AsString();
if (topo == "VK_PRIMITIVE_TOPOLOGY_PATCH_LIST")
topology = Topology::kPatchList;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_POINT_LIST")
topology = Topology::kPointList;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_LINE_LIST")
topology = Topology::kLineList;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY")
topology = Topology::kLineListWithAdjacency;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_LINE_STRIP")
topology = Topology::kLineStrip;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY")
topology = Topology::kLineStripWithAdjacency;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN")
topology = Topology::kTriangleFan;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST")
topology = Topology::kTriangleList;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY")
topology = Topology::kTriangleListWithAdjacency;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP")
topology = Topology::kTriangleStrip;
else if (topo == "VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY")
topology = Topology::kTriangleStripWithAdjacency;
else
return Result("Unknown value for topology command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for topology command: " +
token->ToOriginalString());
pipeline_data_.SetTopology(topology);
return {};
}
Result CommandParser::ProcessPolygonMode() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for polygonMode command");
if (!token->IsString())
return Result("Invalid value for polygonMode command: " +
token->ToOriginalString());
PolygonMode mode = PolygonMode::kFill;
std::string m = token->AsString();
if (m == "VK_POLYGON_MODE_FILL")
mode = PolygonMode::kFill;
else if (m == "VK_POLYGON_MODE_LINE")
mode = PolygonMode::kLine;
else if (m == "VK_POLYGON_MODE_POINT")
mode = PolygonMode::kPoint;
else
return Result("Unknown value for polygonMode command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for polygonMode command: " +
token->ToOriginalString());
pipeline_data_.SetPolygonMode(mode);
return {};
}
Result CommandParser::ProcessLogicOp() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for logicOp command");
if (!token->IsString())
return Result("Invalid value for logicOp command: " +
token->ToOriginalString());
LogicOp op = LogicOp::kClear;
std::string name = token->AsString();
if (name == "VK_LOGIC_OP_CLEAR")
op = LogicOp::kClear;
else if (name == "VK_LOGIC_OP_AND")
op = LogicOp::kAnd;
else if (name == "VK_LOGIC_OP_AND_REVERSE")
op = LogicOp::kAndReverse;
else if (name == "VK_LOGIC_OP_COPY")
op = LogicOp::kCopy;
else if (name == "VK_LOGIC_OP_AND_INVERTED")
op = LogicOp::kAndInverted;
else if (name == "VK_LOGIC_OP_NO_OP")
op = LogicOp::kNoOp;
else if (name == "VK_LOGIC_OP_XOR")
op = LogicOp::kXor;
else if (name == "VK_LOGIC_OP_OR")
op = LogicOp::kOr;
else if (name == "VK_LOGIC_OP_NOR")
op = LogicOp::kNor;
else if (name == "VK_LOGIC_OP_EQUIVALENT")
op = LogicOp::kEquivalent;
else if (name == "VK_LOGIC_OP_INVERT")
op = LogicOp::kInvert;
else if (name == "VK_LOGIC_OP_OR_REVERSE")
op = LogicOp::kOrReverse;
else if (name == "VK_LOGIC_OP_COPY_INVERTED")
op = LogicOp::kCopyInverted;
else if (name == "VK_LOGIC_OP_OR_INVERTED")
op = LogicOp::kOrInverted;
else if (name == "VK_LOGIC_OP_NAND")
op = LogicOp::kNand;
else if (name == "VK_LOGIC_OP_SET")
op = LogicOp::kSet;
else
return Result("Unknown value for logicOp command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for logicOp command: " +
token->ToOriginalString());
pipeline_data_.SetLogicOp(op);
return {};
}
Result CommandParser::ProcessCullMode() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for cullMode command");
if (!token->IsString())
return Result("Invalid value for cullMode command: " +
token->ToOriginalString());
CullMode mode = CullMode::kNone;
while (!token->IsEOS() && !token->IsEOL()) {
std::string name = token->AsString();
if (name == "|") {
// We treat everything as an |.
} else if (name == "VK_CULL_MODE_FRONT_BIT") {
if (mode == CullMode::kNone)
mode = CullMode::kFront;
else if (mode == CullMode::kBack)
mode = CullMode::kFrontAndBack;
} else if (name == "VK_CULL_MODE_BACK_BIT") {
if (mode == CullMode::kNone)
mode = CullMode::kBack;
else if (mode == CullMode::kFront)
mode = CullMode::kFrontAndBack;
} else if (name == "VK_CULL_MODE_FRONT_AND_BACK") {
mode = CullMode::kFrontAndBack;
} else if (name == "VK_CULL_MODE_NONE") {
// Do nothing ...
} else {
return Result("Unknown value for cullMode command: " +
token->ToOriginalString());
}
token = tokenizer_->NextToken();
}
pipeline_data_.SetCullMode(mode);
return {};
}
Result CommandParser::ProcessFrontFace() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for frontFace command");
if (!token->IsString())
return Result("Invalid value for frontFace command: " +
token->ToOriginalString());
FrontFace face = FrontFace::kCounterClockwise;
std::string f = token->AsString();
if (f == "VK_FRONT_FACE_COUNTER_CLOCKWISE")
face = FrontFace::kCounterClockwise;
else if (f == "VK_FRONT_FACE_CLOCKWISE")
face = FrontFace::kClockwise;
else
return Result("Unknown value for frontFace command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for frontFace command: " +
token->ToOriginalString());
pipeline_data_.SetFrontFace(face);
return {};
}
Result CommandParser::ProcessBooleanPipelineData(const std::string& name,
bool* value) {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for " + name + " command");
if (!token->IsString())
return Result("Invalid value for " + name +
" command: " + token->ToOriginalString());
Result r = ParseBoolean(token->AsString(), value);
if (!r.IsSuccess())
return r;
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for " + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ProcessPrimitiveRestartEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("primitiveRestartEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnablePrimitiveRestart(value);
return {};
}
Result CommandParser::ProcessDepthClampEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("depthClampEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableDepthClamp(value);
return {};
}
Result CommandParser::ProcessRasterizerDiscardEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("rasterizerDiscardEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableRasterizerDiscard(value);
return {};
}
Result CommandParser::ProcessDepthBiasEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("depthBiasEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableDepthBias(value);
return {};
}
Result CommandParser::ProcessLogicOpEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("logicOpEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableLogicOp(value);
return {};
}
Result CommandParser::ProcessBlendEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("blendEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableBlend(value);
return {};
}
Result CommandParser::ProcessDepthTestEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("depthTestEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableDepthTest(value);
return {};
}
Result CommandParser::ProcessDepthWriteEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("depthWriteEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableDepthWrite(value);
return {};
}
Result CommandParser::ProcessDepthBoundsTestEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("depthBoundsTestEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableDepthBoundsTest(value);
return {};
}
Result CommandParser::ProcessStencilTestEnable() {
bool value = false;
Result r = ProcessBooleanPipelineData("stencilTestEnable", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetEnableStencilTest(value);
return {};
}
Result CommandParser::ProcessFloatPipelineData(const std::string& name,
float* value) {
assert(value);
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for " + name + " command");
Result r = token->ConvertToDouble();
if (!r.IsSuccess())
return r;
*value = token->AsFloat();
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for " + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ProcessDepthBiasConstantFactor() {
float value = 0.0;
Result r = ProcessFloatPipelineData("depthBiasConstantFactor", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDepthBiasConstantFactor(value);
return {};
}
Result CommandParser::ProcessDepthBiasClamp() {
float value = 0.0;
Result r = ProcessFloatPipelineData("depthBiasClamp", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDepthBiasClamp(value);
return {};
}
Result CommandParser::ProcessDepthBiasSlopeFactor() {
float value = 0.0;
Result r = ProcessFloatPipelineData("depthBiasSlopeFactor", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDepthBiasSlopeFactor(value);
return {};
}
Result CommandParser::ProcessLineWidth() {
float value = 0.0;
Result r = ProcessFloatPipelineData("lineWidth", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetLineWidth(value);
return {};
}
Result CommandParser::ProcessMinDepthBounds() {
float value = 0.0;
Result r = ProcessFloatPipelineData("minDepthBounds", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetMinDepthBounds(value);
return {};
}
Result CommandParser::ProcessMaxDepthBounds() {
float value = 0.0;
Result r = ProcessFloatPipelineData("maxDepthBounds", &value);
if (!r.IsSuccess())
return r;
pipeline_data_.SetMaxDepthBounds(value);
return {};
}
Result CommandParser::ParseBlendFactorName(const std::string& name,
BlendFactor* factor) {
assert(factor);
if (name == "VK_BLEND_FACTOR_ZERO")
*factor = BlendFactor::kZero;
else if (name == "VK_BLEND_FACTOR_ONE")
*factor = BlendFactor::kOne;
else if (name == "VK_BLEND_FACTOR_SRC_COLOR")
*factor = BlendFactor::kSrcColor;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR")
*factor = BlendFactor::kOneMinusSrcColor;
else if (name == "VK_BLEND_FACTOR_DST_COLOR")
*factor = BlendFactor::kDstColor;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR")
*factor = BlendFactor::kOneMinusDstColor;
else if (name == "VK_BLEND_FACTOR_SRC_ALPHA")
*factor = BlendFactor::kSrcAlpha;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA")
*factor = BlendFactor::kOneMinusSrcAlpha;
else if (name == "VK_BLEND_FACTOR_DST_ALPHA")
*factor = BlendFactor::kDstAlpha;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA")
*factor = BlendFactor::kOneMinusDstAlpha;
else if (name == "VK_BLEND_FACTOR_CONSTANT_COLOR")
*factor = BlendFactor::kConstantColor;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR")
*factor = BlendFactor::kOneMinusConstantColor;
else if (name == "VK_BLEND_FACTOR_CONSTANT_ALPHA")
*factor = BlendFactor::kConstantAlpha;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA")
*factor = BlendFactor::kOneMinusConstantAlpha;
else if (name == "VK_BLEND_FACTOR_SRC_ALPHA_SATURATE")
*factor = BlendFactor::kSrcAlphaSaturate;
else if (name == "VK_BLEND_FACTOR_SRC1_COLOR")
*factor = BlendFactor::kSrc1Color;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR")
*factor = BlendFactor::kOneMinusSrc1Color;
else if (name == "VK_BLEND_FACTOR_SRC1_ALPHA")
*factor = BlendFactor::kSrc1Alpha;
else if (name == "VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA")
*factor = BlendFactor::kOneMinusSrc1Alpha;
else
return Result("Unknown BlendFactor provided: " + name);
return {};
}
Result CommandParser::ParseBlendFactor(const std::string& name,
BlendFactor* factor) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
if (!token->IsString())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
Result r = ParseBlendFactorName(token->AsString(), factor);
if (!r.IsSuccess())
return r;
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result(std::string("Extra parameter for ") + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ProcessSrcAlphaBlendFactor() {
BlendFactor factor = BlendFactor::kZero;
Result r = ParseBlendFactor("srcAlphaBlendFactor", &factor);
if (!r.IsSuccess())
return r;
pipeline_data_.SetSrcAlphaBlendFactor(factor);
return {};
}
Result CommandParser::ProcessDstAlphaBlendFactor() {
BlendFactor factor = BlendFactor::kZero;
Result r = ParseBlendFactor("dstAlphaBlendFactor", &factor);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDstAlphaBlendFactor(factor);
return {};
}
Result CommandParser::ProcessSrcColorBlendFactor() {
BlendFactor factor = BlendFactor::kZero;
Result r = ParseBlendFactor("srcColorBlendFactor", &factor);
if (!r.IsSuccess())
return r;
pipeline_data_.SetSrcColorBlendFactor(factor);
return {};
}
Result CommandParser::ProcessDstColorBlendFactor() {
BlendFactor factor = BlendFactor::kZero;
Result r = ParseBlendFactor("dstColorBlendFactor", &factor);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDstColorBlendFactor(factor);
return {};
}
Result CommandParser::ParseBlendOpName(const std::string& name, BlendOp* op) {
assert(op);
if (name == "VK_BLEND_OP_ADD")
*op = BlendOp::kAdd;
else if (name == "VK_BLEND_OP_ADD")
*op = BlendOp::kAdd;
else if (name == "VK_BLEND_OP_SUBTRACT")
*op = BlendOp::kSubtract;
else if (name == "VK_BLEND_OP_REVERSE_SUBTRACT")
*op = BlendOp::kReverseSubtract;
else if (name == "VK_BLEND_OP_MIN")
*op = BlendOp::kMin;
else if (name == "VK_BLEND_OP_MAX")
*op = BlendOp::kMax;
else if (name == "VK_BLEND_OP_ZERO_EXT")
*op = BlendOp::kZero;
else if (name == "VK_BLEND_OP_SRC_EXT")
*op = BlendOp::kSrc;
else if (name == "VK_BLEND_OP_DST_EXT")
*op = BlendOp::kDst;
else if (name == "VK_BLEND_OP_SRC_OVER_EXT")
*op = BlendOp::kSrcOver;
else if (name == "VK_BLEND_OP_DST_OVER_EXT")
*op = BlendOp::kDstOver;
else if (name == "VK_BLEND_OP_SRC_IN_EXT")
*op = BlendOp::kSrcIn;
else if (name == "VK_BLEND_OP_DST_IN_EXT")
*op = BlendOp::kDstIn;
else if (name == "VK_BLEND_OP_SRC_OUT_EXT")
*op = BlendOp::kSrcOut;
else if (name == "VK_BLEND_OP_DST_OUT_EXT")
*op = BlendOp::kDstOut;
else if (name == "VK_BLEND_OP_SRC_ATOP_EXT")
*op = BlendOp::kSrcAtop;
else if (name == "VK_BLEND_OP_DST_ATOP_EXT")
*op = BlendOp::kDstAtop;
else if (name == "VK_BLEND_OP_XOR_EXT")
*op = BlendOp::kXor;
else if (name == "VK_BLEND_OP_MULTIPLY_EXT")
*op = BlendOp::kMultiply;
else if (name == "VK_BLEND_OP_SCREEN_EXT")
*op = BlendOp::kScreen;
else if (name == "VK_BLEND_OP_OVERLAY_EXT")
*op = BlendOp::kOverlay;
else if (name == "VK_BLEND_OP_DARKEN_EXT")
*op = BlendOp::kDarken;
else if (name == "VK_BLEND_OP_LIGHTEN_EXT")
*op = BlendOp::kLighten;
else if (name == "VK_BLEND_OP_COLORDODGE_EXT")
*op = BlendOp::kColorDodge;
else if (name == "VK_BLEND_OP_COLORBURN_EXT")
*op = BlendOp::kColorBurn;
else if (name == "VK_BLEND_OP_HARDLIGHT_EXT")
*op = BlendOp::kHardLight;
else if (name == "VK_BLEND_OP_SOFTLIGHT_EXT")
*op = BlendOp::kSoftLight;
else if (name == "VK_BLEND_OP_DIFFERENCE_EXT")
*op = BlendOp::kDifference;
else if (name == "VK_BLEND_OP_EXCLUSION_EXT")
*op = BlendOp::kExclusion;
else if (name == "VK_BLEND_OP_INVERT_EXT")
*op = BlendOp::kInvert;
else if (name == "VK_BLEND_OP_INVERT_RGB_EXT")
*op = BlendOp::kInvertRGB;
else if (name == "VK_BLEND_OP_LINEARDODGE_EXT")
*op = BlendOp::kLinearDodge;
else if (name == "VK_BLEND_OP_LINEARBURN_EXT")
*op = BlendOp::kLinearBurn;
else if (name == "VK_BLEND_OP_VIVIDLIGHT_EXT")
*op = BlendOp::kVividLight;
else if (name == "VK_BLEND_OP_LINEARLIGHT_EXT")
*op = BlendOp::kLinearLight;
else if (name == "VK_BLEND_OP_PINLIGHT_EXT")
*op = BlendOp::kPinLight;
else if (name == "VK_BLEND_OP_HARDMIX_EXT")
*op = BlendOp::kHardMix;
else if (name == "VK_BLEND_OP_HSL_HUE_EXT")
*op = BlendOp::kHslHue;
else if (name == "VK_BLEND_OP_HSL_SATURATION_EXT")
*op = BlendOp::kHslSaturation;
else if (name == "VK_BLEND_OP_HSL_COLOR_EXT")
*op = BlendOp::kHslColor;
else if (name == "VK_BLEND_OP_HSL_LUMINOSITY_EXT")
*op = BlendOp::kHslLuminosity;
else if (name == "VK_BLEND_OP_PLUS_EXT")
*op = BlendOp::kPlus;
else if (name == "VK_BLEND_OP_PLUS_CLAMPED_EXT")
*op = BlendOp::kPlusClamped;
else if (name == "VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT")
*op = BlendOp::kPlusClampedAlpha;
else if (name == "VK_BLEND_OP_PLUS_DARKER_EXT")
*op = BlendOp::kPlusDarker;
else if (name == "VK_BLEND_OP_MINUS_EXT")
*op = BlendOp::kMinus;
else if (name == "VK_BLEND_OP_MINUS_CLAMPED_EXT")
*op = BlendOp::kMinusClamped;
else if (name == "VK_BLEND_OP_CONTRAST_EXT")
*op = BlendOp::kContrast;
else if (name == "VK_BLEND_OP_INVERT_OVG_EXT")
*op = BlendOp::kInvertOvg;
else if (name == "VK_BLEND_OP_RED_EXT")
*op = BlendOp::kRed;
else if (name == "VK_BLEND_OP_GREEN_EXT")
*op = BlendOp::kGreen;
else if (name == "VK_BLEND_OP_BLUE_EXT")
*op = BlendOp::kBlue;
else
return Result("Unknown BlendOp provided: " + name);
return {};
}
Result CommandParser::ParseBlendOp(const std::string& name, BlendOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
if (!token->IsString())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
Result r = ParseBlendOpName(token->AsString(), op);
if (!r.IsSuccess())
return r;
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result(std::string("Extra parameter for ") + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ProcessColorBlendOp() {
BlendOp op = BlendOp::kAdd;
Result r = ParseBlendOp("colorBlendOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetColorBlendOp(op);
return {};
}
Result CommandParser::ProcessAlphaBlendOp() {
BlendOp op = BlendOp::kAdd;
Result r = ParseBlendOp("alphaBlendOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetAlphaBlendOp(op);
return {};
}
Result CommandParser::ParseCompareOp(const std::string& name, CompareOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
if (!token->IsString())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
Result r = ParseCompareOpName(token->AsString(), op);
if (!r.IsSuccess())
return r;
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result(std::string("Extra parameter for ") + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ParseCompareOpName(const std::string& name,
CompareOp* op) {
assert(op);
if (name == "VK_COMPARE_OP_NEVER")
*op = CompareOp::kNever;
else if (name == "VK_COMPARE_OP_LESS")
*op = CompareOp::kLess;
else if (name == "VK_COMPARE_OP_EQUAL")
*op = CompareOp::kEqual;
else if (name == "VK_COMPARE_OP_LESS_OR_EQUAL")
*op = CompareOp::kLessOrEqual;
else if (name == "VK_COMPARE_OP_GREATER")
*op = CompareOp::kGreater;
else if (name == "VK_COMPARE_OP_NOT_EQUAL")
*op = CompareOp::kNotEqual;
else if (name == "VK_COMPARE_OP_GREATER_OR_EQUAL")
*op = CompareOp::kGreaterOrEqual;
else if (name == "VK_COMPARE_OP_ALWAYS")
*op = CompareOp::kAlways;
else
return Result("Unknown CompareOp provided: " + name);
return {};
}
Result CommandParser::ProcessDepthCompareOp() {
CompareOp op = CompareOp::kNever;
Result r = ParseCompareOp("depthCompareOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetDepthCompareOp(op);
return {};
}
Result CommandParser::ProcessFrontCompareOp() {
CompareOp op = CompareOp::kNever;
Result r = ParseCompareOp("front.compareOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetFrontCompareOp(op);
return {};
}
Result CommandParser::ProcessBackCompareOp() {
CompareOp op = CompareOp::kNever;
Result r = ParseCompareOp("back.compareOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetBackCompareOp(op);
return {};
}
Result CommandParser::ParseStencilOp(const std::string& name, StencilOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
if (!token->IsString())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
Result r = ParseStencilOpName(token->AsString(), op);
if (!r.IsSuccess())
return r;
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result(std::string("Extra parameter for ") + name +
" command: " + token->ToOriginalString());
return {};
}
Result CommandParser::ParseStencilOpName(const std::string& name,
StencilOp* op) {
assert(op);
if (name == "VK_STENCIL_OP_KEEP")
*op = StencilOp::kKeep;
else if (name == "VK_STENCIL_OP_ZERO")
*op = StencilOp::kZero;
else if (name == "VK_STENCIL_OP_REPLACE")
*op = StencilOp::kReplace;
else if (name == "VK_STENCIL_OP_INCREMENT_AND_CLAMP")
*op = StencilOp::kIncrementAndClamp;
else if (name == "VK_STENCIL_OP_DECREMENT_AND_CLAMP")
*op = StencilOp::kDecrementAndClamp;
else if (name == "VK_STENCIL_OP_INVERT")
*op = StencilOp::kInvert;
else if (name == "VK_STENCIL_OP_INCREMENT_AND_WRAP")
*op = StencilOp::kIncrementAndWrap;
else if (name == "VK_STENCIL_OP_DECREMENT_AND_WRAP")
*op = StencilOp::kDecrementAndWrap;
else
return Result("Unknown StencilOp provided: " + name);
return {};
}
Result CommandParser::ProcessFrontFailOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("front.failOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetFrontFailOp(op);
return {};
}
Result CommandParser::ProcessFrontPassOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("front.passOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetFrontPassOp(op);
return {};
}
Result CommandParser::ProcessFrontDepthFailOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("front.depthFailOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetFrontDepthFailOp(op);
return {};
}
Result CommandParser::ProcessBackFailOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("back.failOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetBackFailOp(op);
return {};
}
Result CommandParser::ProcessBackPassOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("back.passOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetBackPassOp(op);
return {};
}
Result CommandParser::ProcessBackDepthFailOp() {
StencilOp op = StencilOp::kKeep;
Result r = ParseStencilOp("back.depthFailOp", &op);
if (!r.IsSuccess())
return r;
pipeline_data_.SetBackDepthFailOp(op);
return {};
}
Result CommandParser::ProcessFrontCompareMask() {
return Result("front.compareMask not implemented");
}
Result CommandParser::ProcessFrontWriteMask() {
return Result("front.writeMask not implemented");
}
Result CommandParser::ProcessBackCompareMask() {
return Result("back.compareMask not implemented");
}
Result CommandParser::ProcessBackWriteMask() {
return Result("back.writeMask not implemented");
}
Result CommandParser::ProcessFrontReference() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing parameter for front.reference command");
if (!token->IsInteger())
return Result("Invalid parameter for front.reference command: " +
token->ToOriginalString());
pipeline_data_.SetFrontReference(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for front.reference command: " +
token->ToOriginalString());
return {};
}
Result CommandParser::ProcessBackReference() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing parameter for back.reference command");
if (!token->IsInteger())
return Result("Invalid parameter for back.reference command: " +
token->ToOriginalString());
pipeline_data_.SetBackReference(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsEOS() && !token->IsEOL())
return Result("Extra parameter for back.reference command: " +
token->ToOriginalString());
return {};
}
Result CommandParser::ProcessColorWriteMask() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing parameter for colorWriteMask command");
if (!token->IsString())
return Result("Invalid parameter for colorWriteMask command: " +
token->ToOriginalString());
uint8_t mask = 0;
while (!token->IsEOS() && !token->IsEOL()) {
std::string name = token->AsString();
if (name == "|") {
// We treat everything as an |.
} else if (name == "VK_COLOR_COMPONENT_R_BIT") {
mask |= kColorMaskR;
} else if (name == "VK_COLOR_COMPONENT_G_BIT") {
mask |= kColorMaskG;
} else if (name == "VK_COLOR_COMPONENT_B_BIT") {
mask |= kColorMaskB;
} else if (name == "VK_COLOR_COMPONENT_A_BIT") {
mask |= kColorMaskA;
} else {
return Result("Unknown parameter for colorWriteMask command: " + name);
}
token = tokenizer_->NextToken();
}
pipeline_data_.SetColorWriteMask(mask);
return {};
}
Result CommandParser::ParseComparator(const std::string& name,
ProbeSSBOCommand::Comparator* op) {
if (name == "==")
*op = ProbeSSBOCommand::Comparator::kEqual;
else if (name == "!=")
*op = ProbeSSBOCommand::Comparator::kNotEqual;
else if (name == "~=")
*op = ProbeSSBOCommand::Comparator::kFuzzyEqual;
else if (name == "<")
*op = ProbeSSBOCommand::Comparator::kLess;
else if (name == "<=")
*op = ProbeSSBOCommand::Comparator::kLessOrEqual;
else if (name == ">")
*op = ProbeSSBOCommand::Comparator::kGreater;
else if (name == ">=")
*op = ProbeSSBOCommand::Comparator::kGreaterOrEqual;
else
return Result("Invalid comparator: " + name);
return {};
}
Result CommandParser::ProcessProbeSSBO() {
size_t cur_line = tokenizer_->GetCurrentLine();
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing values for probe ssbo command");
if (!token->IsString())
return Result("Invalid type for probe ssbo command: " +
token->ToOriginalString());
DatumTypeParser tp;
auto fmt = tp.Parse(token->AsString());
if (!fmt)
return Result("Invalid type provided: " + token->AsString());
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("Invalid binding value for probe ssbo command: " +
token->ToOriginalString());
uint32_t val = token->AsUint32();
uint32_t set = 0;
uint32_t binding = 0;
token = tokenizer_->NextToken();
if (token->IsString()) {
auto& str = token->AsString();
if (str.size() >= 2 && str[0] == ':') {
set = val;
auto substr = str.substr(1, str.size());
uint64_t binding_val = strtoul(substr.c_str(), nullptr, 10);
if (binding_val > std::numeric_limits<uint32_t>::max())
return Result("binding value too large in probe ssbo command: " +
token->ToOriginalString());
binding = static_cast<uint32_t>(binding_val);
} else {
return Result("Invalid value for probe ssbo command: " +
token->ToOriginalString());
}
token = tokenizer_->NextToken();
} else {
binding = val;
}
auto* buffer = pipeline_->GetBufferForBinding(set, binding);
if (!buffer) {
return Result("unable to find buffer at descriptor set " +
std::to_string(set) + " and binding " +
std::to_string(binding));
}
if (buffer->FormatIsDefault() || !buffer->GetFormat())
buffer->SetFormat(MakeUnique<Format>(*fmt));
else if (buffer->GetFormat() && !buffer->GetFormat()->Equal(fmt.get()))
return Result("probe format does not match buffer format");
auto cmd = MakeUnique<ProbeSSBOCommand>(buffer);
cmd->SetLine(cur_line);
cmd->SetTolerances(current_tolerances_);
cmd->SetFormat(std::move(fmt));
cmd->SetDescriptorSet(set);
cmd->SetBinding(binding);
if (!token->IsInteger())
return Result("Invalid offset for probe ssbo command: " +
token->ToOriginalString());
cmd->SetOffset(token->AsUint32());
token = tokenizer_->NextToken();
if (!token->IsString())
return Result("Invalid comparator for probe ssbo command: " +
token->ToOriginalString());
ProbeSSBOCommand::Comparator comp = ProbeSSBOCommand::Comparator::kEqual;
Result r = ParseComparator(token->AsString(), &comp);
if (!r.IsSuccess())
return r;
cmd->SetComparator(comp);
std::vector<Value> values;
r = ParseValues("probe ssbo", cmd->GetFormat(), &values);
if (!r.IsSuccess())
return r;
cmd->SetValues(std::move(values));
commands_.push_back(std::move(cmd));
return {};
}
} // namespace vkscript
} // namespace amber