2017-04-20 18:38:20 +00:00
|
|
|
// Copyright 2017 The NXT 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 "CommandBuffer.h"
|
|
|
|
|
|
|
|
#include "BindGroup.h"
|
|
|
|
#include "BindGroupLayout.h"
|
|
|
|
#include "Buffer.h"
|
|
|
|
#include "Commands.h"
|
|
|
|
#include "Device.h"
|
|
|
|
#include "InputState.h"
|
|
|
|
#include "Pipeline.h"
|
|
|
|
#include "PipelineLayout.h"
|
|
|
|
#include "Texture.h"
|
|
|
|
|
|
|
|
#include <cstring>
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
namespace backend {
|
|
|
|
|
|
|
|
CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder)
|
|
|
|
: device(builder->device),
|
|
|
|
buffersTransitioned(std::move(builder->buffersTransitioned)),
|
|
|
|
texturesTransitioned(std::move(builder->texturesTransitioned)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferBase::ValidateResourceUsagesImmediate() {
|
|
|
|
for (auto buffer : buffersTransitioned) {
|
|
|
|
if (buffer->IsFrozen()) {
|
|
|
|
device->HandleError("Command buffer: cannot transition buffer with frozen usage");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto texture : texturesTransitioned) {
|
|
|
|
if (texture->IsFrozen()) {
|
|
|
|
device->HandleError("Command buffer: cannot transition texture with frozen usage");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreeCommands(CommandIterator* commands) {
|
|
|
|
Command type;
|
|
|
|
while(commands->NextCommandId(&type)) {
|
|
|
|
switch (type) {
|
|
|
|
case Command::CopyBufferToTexture:
|
|
|
|
{
|
|
|
|
CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>();
|
|
|
|
copy->~CopyBufferToTextureCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::Dispatch:
|
|
|
|
{
|
|
|
|
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
|
|
|
|
dispatch->~DispatchCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::DrawArrays:
|
|
|
|
{
|
|
|
|
DrawArraysCmd* draw = commands->NextCommand<DrawArraysCmd>();
|
|
|
|
draw->~DrawArraysCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::DrawElements:
|
|
|
|
{
|
|
|
|
DrawElementsCmd* draw = commands->NextCommand<DrawElementsCmd>();
|
|
|
|
draw->~DrawElementsCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::SetPipeline:
|
|
|
|
{
|
|
|
|
SetPipelineCmd* cmd = commands->NextCommand<SetPipelineCmd>();
|
|
|
|
cmd->~SetPipelineCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::SetPushConstants:
|
|
|
|
{
|
|
|
|
SetPushConstantsCmd* cmd = commands->NextCommand<SetPushConstantsCmd>();
|
|
|
|
commands->NextData<uint32_t>(cmd->count);
|
|
|
|
cmd->~SetPushConstantsCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::SetBindGroup:
|
|
|
|
{
|
|
|
|
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
|
|
|
|
cmd->~SetBindGroupCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::SetIndexBuffer:
|
|
|
|
{
|
|
|
|
SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
|
|
|
|
cmd->~SetIndexBufferCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::SetVertexBuffers:
|
|
|
|
{
|
|
|
|
SetVertexBuffersCmd* cmd = commands->NextCommand<SetVertexBuffersCmd>();
|
|
|
|
auto buffers = commands->NextData<Ref<BufferBase>>(cmd->count);
|
|
|
|
for (size_t i = 0; i < cmd->count; ++i) {
|
|
|
|
(&buffers[i])->~Ref<BufferBase>();
|
|
|
|
}
|
|
|
|
commands->NextData<uint32_t>(cmd->count);
|
|
|
|
cmd->~SetVertexBuffersCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::TransitionBufferUsage:
|
|
|
|
{
|
|
|
|
TransitionBufferUsageCmd* cmd = commands->NextCommand<TransitionBufferUsageCmd>();
|
|
|
|
cmd->~TransitionBufferUsageCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command::TransitionTextureUsage:
|
|
|
|
{
|
|
|
|
TransitionTextureUsageCmd* cmd = commands->NextCommand<TransitionTextureUsageCmd>();
|
|
|
|
cmd->~TransitionTextureUsageCmd();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
commands->DataWasDestroyed();
|
|
|
|
}
|
|
|
|
|
2017-05-08 08:52:11 +00:00
|
|
|
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device) {
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CommandBufferBuilder::~CommandBufferBuilder() {
|
2017-05-08 13:17:44 +00:00
|
|
|
if (!commandsAcquired) {
|
2017-04-20 18:38:20 +00:00
|
|
|
MoveToIterator();
|
|
|
|
FreeCommands(&iterator);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ValidationAspect {
|
|
|
|
VALIDATION_ASPECT_RENDER_PIPELINE,
|
|
|
|
VALIDATION_ASPECT_COMPUTE_PIPELINE,
|
|
|
|
VALIDATION_ASPECT_BINDGROUPS,
|
|
|
|
VALIDATION_ASPECT_VERTEX_BUFFERS,
|
|
|
|
VALIDATION_ASPECT_INDEX_BUFFER,
|
|
|
|
|
|
|
|
VALIDATION_ASPECT_COUNT,
|
|
|
|
};
|
|
|
|
|
|
|
|
using ValidationAspects = std::bitset<VALIDATION_ASPECT_COUNT>;
|
|
|
|
|
|
|
|
bool CommandBufferBuilder::ValidateGetResult() {
|
|
|
|
MoveToIterator();
|
|
|
|
|
|
|
|
ValidationAspects aspects;
|
|
|
|
std::bitset<kMaxBindGroups> bindgroupsSet;
|
|
|
|
std::bitset<kMaxVertexInputs> inputsSet;
|
|
|
|
PipelineBase* lastPipeline = nullptr;
|
|
|
|
|
|
|
|
std::map<BufferBase*, nxt::BufferUsageBit> mostRecentBufferUsages;
|
|
|
|
auto bufferHasGuaranteedUsageBit = [&](BufferBase* buffer, nxt::BufferUsageBit usage) -> bool {
|
|
|
|
assert(usage != nxt::BufferUsageBit::None && nxt::HasZeroOrOneBits(usage));
|
|
|
|
if (buffer->HasFrozenUsage(usage)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto it = mostRecentBufferUsages.find(buffer);
|
|
|
|
return it != mostRecentBufferUsages.end() && (it->second & usage);
|
|
|
|
};
|
|
|
|
|
|
|
|
std::map<TextureBase*, nxt::TextureUsageBit> mostRecentTextureUsages;
|
|
|
|
auto textureHasGuaranteedUsageBit = [&](TextureBase* texture, nxt::TextureUsageBit usage) -> bool {
|
|
|
|
assert(usage != nxt::TextureUsageBit::None && nxt::HasZeroOrOneBits(usage));
|
|
|
|
if (texture->HasFrozenUsage(usage)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto it = mostRecentTextureUsages.find(texture);
|
|
|
|
return it != mostRecentTextureUsages.end() && (it->second & usage);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto validateBindGroupUsages = [&](BindGroupBase* group) -> bool {
|
|
|
|
const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
|
|
|
|
for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) {
|
|
|
|
if (!layoutInfo.mask[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nxt::BindingType type = layoutInfo.types[i];
|
|
|
|
switch (type) {
|
|
|
|
case nxt::BindingType::UniformBuffer:
|
|
|
|
case nxt::BindingType::StorageBuffer:
|
|
|
|
{
|
|
|
|
nxt::BufferUsageBit requiredUsage;
|
|
|
|
switch (type) {
|
|
|
|
case nxt::BindingType::UniformBuffer:
|
|
|
|
requiredUsage = nxt::BufferUsageBit::Uniform;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nxt::BindingType::StorageBuffer:
|
|
|
|
requiredUsage = nxt::BufferUsageBit::Storage;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buffer = group->GetBindingAsBufferView(i)->GetBuffer();
|
|
|
|
if (!bufferHasGuaranteedUsageBit(buffer, requiredUsage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Can't guarantee buffer usage needed by bind group");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case nxt::BindingType::SampledTexture:
|
|
|
|
{
|
|
|
|
auto requiredUsage = nxt::TextureUsageBit::Sampled;
|
|
|
|
|
|
|
|
auto texture = group->GetBindingAsTextureView(i)->GetTexture();
|
|
|
|
if (!textureHasGuaranteedUsageBit(texture, requiredUsage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Can't guarantee texture usage needed by bind group");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case nxt::BindingType::Sampler:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
Command type;
|
|
|
|
while(iterator.NextCommandId(&type)) {
|
|
|
|
switch (type) {
|
|
|
|
case Command::CopyBufferToTexture:
|
|
|
|
{
|
|
|
|
CopyBufferToTextureCmd* copy = iterator.NextCommand<CopyBufferToTextureCmd>();
|
|
|
|
BufferBase* buffer = copy->buffer.Get();
|
2017-05-09 14:57:52 +00:00
|
|
|
uint32_t bufferOffset = copy->bufferOffset;
|
2017-04-20 18:38:20 +00:00
|
|
|
TextureBase* texture = copy->texture.Get();
|
|
|
|
uint64_t width = copy->width;
|
|
|
|
uint64_t height = copy->height;
|
|
|
|
uint64_t depth = copy->depth;
|
|
|
|
uint64_t x = copy->x;
|
|
|
|
uint64_t y = copy->y;
|
|
|
|
uint64_t z = copy->z;
|
|
|
|
uint32_t level = copy->level;
|
|
|
|
|
|
|
|
if (!bufferHasGuaranteedUsageBit(buffer, nxt::BufferUsageBit::TransferSrc)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer needs the transfer source usage bit");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!textureHasGuaranteedUsageBit(texture, nxt::TextureUsageBit::TransferDst)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Texture needs the transfer destination usage bit");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width == 0 || height == 0 || depth == 0) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Empty copy");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(cwallez@chromium.org): check for overflows
|
|
|
|
uint64_t pixelSize = TextureFormatPixelSize(texture->GetFormat());
|
|
|
|
uint64_t dataSize = width * height * depth * pixelSize;
|
|
|
|
|
2017-05-09 14:57:52 +00:00
|
|
|
if (dataSize + static_cast<uint64_t>(bufferOffset) > static_cast<uint64_t>(buffer->GetSize())) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Copy would read after end of the buffer");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x + width > static_cast<uint64_t>(texture->GetWidth()) ||
|
|
|
|
y + height > static_cast<uint64_t>(texture->GetHeight()) ||
|
|
|
|
z + depth > static_cast<uint64_t>(texture->GetDepth()) ||
|
|
|
|
level > texture->GetNumMipLevels()) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Copy would write outside of the texture");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::Dispatch:
|
|
|
|
{
|
|
|
|
DispatchCmd* cmd = iterator.NextCommand<DispatchCmd>();
|
|
|
|
|
|
|
|
constexpr ValidationAspects requiredDispatchAspects =
|
|
|
|
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE |
|
|
|
|
1 << VALIDATION_ASPECT_BINDGROUPS |
|
|
|
|
1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
|
|
|
|
|
|
|
|
if ((requiredDispatchAspects & ~aspects).any()) {
|
|
|
|
// Compute the lazily computed aspects
|
|
|
|
if (bindgroupsSet.all()) {
|
|
|
|
aspects.set(VALIDATION_ASPECT_BINDGROUPS);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto requiredInputs = lastPipeline->GetInputState()->GetInputsSetMask();
|
|
|
|
if ((inputsSet & ~requiredInputs).none()) {
|
|
|
|
aspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check again if anything is missing
|
|
|
|
if ((requiredDispatchAspects & ~aspects).any()) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Some dispatch state is missing");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::DrawArrays:
|
|
|
|
case Command::DrawElements:
|
|
|
|
{
|
|
|
|
constexpr ValidationAspects requiredDrawAspects =
|
|
|
|
1 << VALIDATION_ASPECT_RENDER_PIPELINE |
|
|
|
|
1 << VALIDATION_ASPECT_BINDGROUPS |
|
|
|
|
1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
|
|
|
|
|
|
|
|
if ((requiredDrawAspects & ~aspects).any()) {
|
|
|
|
// Compute the lazily computed aspects
|
|
|
|
if (bindgroupsSet.all()) {
|
|
|
|
aspects.set(VALIDATION_ASPECT_BINDGROUPS);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto requiredInputs = lastPipeline->GetInputState()->GetInputsSetMask();
|
|
|
|
if ((inputsSet & ~requiredInputs).none()) {
|
|
|
|
aspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check again if anything is missing
|
|
|
|
if ((requiredDrawAspects & ~aspects).any()) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Some draw state is missing");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == Command::DrawArrays) {
|
|
|
|
DrawArraysCmd* draw = iterator.NextCommand<DrawArraysCmd>();
|
|
|
|
} else {
|
|
|
|
ASSERT(type == Command::DrawElements);
|
|
|
|
DrawElementsCmd* draw = iterator.NextCommand<DrawElementsCmd>();
|
|
|
|
|
|
|
|
if (!aspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Draw elements requires an index buffer");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::SetPipeline:
|
|
|
|
{
|
|
|
|
SetPipelineCmd* cmd = iterator.NextCommand<SetPipelineCmd>();
|
|
|
|
PipelineBase* pipeline = cmd->pipeline.Get();
|
|
|
|
PipelineLayoutBase* layout = pipeline->GetLayout();
|
|
|
|
|
|
|
|
if (pipeline->IsCompute()) {
|
|
|
|
aspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE);
|
|
|
|
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
|
|
|
|
} else {
|
|
|
|
aspects.set(VALIDATION_ASPECT_RENDER_PIPELINE);
|
|
|
|
aspects.reset(VALIDATION_ASPECT_COMPUTE_PIPELINE);
|
|
|
|
}
|
|
|
|
aspects.reset(VALIDATION_ASPECT_BINDGROUPS);
|
|
|
|
aspects.reset(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
|
|
|
bindgroupsSet = ~layout->GetBindGroupsLayoutMask();
|
|
|
|
|
|
|
|
// Only bindgroups that were not the same layout in the last pipeline need to be set again.
|
|
|
|
if (lastPipeline) {
|
|
|
|
PipelineLayoutBase* lastLayout = lastPipeline->GetLayout();
|
|
|
|
for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
|
|
|
|
if (lastLayout->GetBindGroupLayout(i) == layout->GetBindGroupLayout(i)) {
|
|
|
|
bindgroupsSet |= uint64_t(1) << i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lastPipeline = pipeline;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::SetPushConstants:
|
|
|
|
{
|
|
|
|
SetPushConstantsCmd* cmd = iterator.NextCommand<SetPushConstantsCmd>();
|
|
|
|
iterator.NextData<uint32_t>(cmd->count);
|
|
|
|
if (cmd->count + cmd->offset > kMaxPushConstants) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Setting pushconstants past the limit");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::SetBindGroup:
|
|
|
|
{
|
|
|
|
SetBindGroupCmd* cmd = iterator.NextCommand<SetBindGroupCmd>();
|
|
|
|
uint32_t index = cmd->index;
|
|
|
|
|
|
|
|
if (cmd->group->GetLayout() != lastPipeline->GetLayout()->GetBindGroupLayout(index)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Bind group layout mismatch");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!validateBindGroupUsages(cmd->group.Get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bindgroupsSet |= uint64_t(1) << index;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::SetIndexBuffer:
|
|
|
|
{
|
|
|
|
SetIndexBufferCmd* cmd = iterator.NextCommand<SetIndexBufferCmd>();
|
|
|
|
auto buffer = cmd->buffer;
|
|
|
|
auto usage = nxt::BufferUsageBit::Index;
|
|
|
|
if (!bufferHasGuaranteedUsageBit(buffer.Get(), usage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer needs the index usage bit to be guaranteed");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::SetVertexBuffers:
|
|
|
|
{
|
|
|
|
SetVertexBuffersCmd* cmd = iterator.NextCommand<SetVertexBuffersCmd>();
|
|
|
|
auto buffers = iterator.NextData<Ref<BufferBase>>(cmd->count);
|
|
|
|
iterator.NextData<uint32_t>(cmd->count);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < cmd->count; ++i) {
|
|
|
|
auto buffer = buffers[i];
|
|
|
|
auto usage = nxt::BufferUsageBit::Vertex;
|
|
|
|
if (!bufferHasGuaranteedUsageBit(buffer.Get(), usage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer needs vertex usage bit to be guaranteed");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
inputsSet.set(cmd->startSlot + i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::TransitionBufferUsage:
|
|
|
|
{
|
|
|
|
TransitionBufferUsageCmd* cmd = iterator.NextCommand<TransitionBufferUsageCmd>();
|
|
|
|
auto buffer = cmd->buffer.Get();
|
|
|
|
auto usage = cmd->usage;
|
|
|
|
|
|
|
|
if (!cmd->buffer->IsTransitionPossible(cmd->usage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer frozen or usage not allowed");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mostRecentBufferUsages[buffer] = usage;
|
|
|
|
|
|
|
|
buffersTransitioned.insert(buffer);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command::TransitionTextureUsage:
|
|
|
|
{
|
|
|
|
TransitionTextureUsageCmd* cmd = iterator.NextCommand<TransitionTextureUsageCmd>();
|
|
|
|
auto texture = cmd->texture.Get();
|
|
|
|
auto usage = cmd->usage;
|
|
|
|
|
|
|
|
if (!cmd->texture->IsTransitionPossible(cmd->usage)) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Texture frozen or usage not allowed");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mostRecentTextureUsages[texture] = usage;
|
|
|
|
|
|
|
|
texturesTransitioned.insert(texture);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CommandIterator CommandBufferBuilder::AcquireCommands() {
|
2017-05-08 13:17:44 +00:00
|
|
|
ASSERT(!commandsAcquired);
|
|
|
|
commandsAcquired = true;
|
2017-04-20 18:38:20 +00:00
|
|
|
return std::move(iterator);
|
|
|
|
}
|
|
|
|
|
2017-05-08 13:17:44 +00:00
|
|
|
CommandBufferBase* CommandBufferBuilder::GetResultImpl() {
|
2017-04-20 18:38:20 +00:00
|
|
|
MoveToIterator();
|
|
|
|
return device->CreateCommandBuffer(this);
|
|
|
|
}
|
|
|
|
|
2017-05-09 14:57:52 +00:00
|
|
|
void CommandBufferBuilder::CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
|
|
|
|
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
|
2017-04-20 18:38:20 +00:00
|
|
|
uint32_t width, uint32_t height, uint32_t depth, uint32_t level) {
|
|
|
|
CopyBufferToTextureCmd* copy = allocator.Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
|
|
|
|
new(copy) CopyBufferToTextureCmd;
|
|
|
|
copy->buffer = buffer;
|
2017-05-09 14:57:52 +00:00
|
|
|
copy->bufferOffset = bufferOffset;
|
2017-04-20 18:38:20 +00:00
|
|
|
copy->texture = texture;
|
|
|
|
copy->x = x;
|
|
|
|
copy->y = y;
|
|
|
|
copy->z = z;
|
|
|
|
copy->width = width;
|
|
|
|
copy->height = height;
|
|
|
|
copy->depth = depth;
|
|
|
|
copy->level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
|
|
|
DispatchCmd* dispatch = allocator.Allocate<DispatchCmd>(Command::Dispatch);
|
|
|
|
new(dispatch) DispatchCmd;
|
|
|
|
dispatch->x = x;
|
|
|
|
dispatch->y = y;
|
|
|
|
dispatch->z = z;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::DrawArrays(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
|
|
|
|
DrawArraysCmd* draw = allocator.Allocate<DrawArraysCmd>(Command::DrawArrays);
|
|
|
|
new(draw) DrawArraysCmd;
|
|
|
|
draw->vertexCount = vertexCount;
|
|
|
|
draw->instanceCount = instanceCount;
|
|
|
|
draw->firstVertex = firstVertex;
|
|
|
|
draw->firstInstance = firstInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::DrawElements(uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance) {
|
|
|
|
DrawElementsCmd* draw = allocator.Allocate<DrawElementsCmd>(Command::DrawElements);
|
|
|
|
new(draw) DrawElementsCmd;
|
|
|
|
draw->indexCount = indexCount;
|
|
|
|
draw->instanceCount = instanceCount;
|
|
|
|
draw->firstIndex = firstIndex;
|
|
|
|
draw->firstInstance = firstInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::SetPipeline(PipelineBase* pipeline) {
|
|
|
|
SetPipelineCmd* cmd = allocator.Allocate<SetPipelineCmd>(Command::SetPipeline);
|
|
|
|
new(cmd) SetPipelineCmd;
|
|
|
|
cmd->pipeline = pipeline;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::SetPushConstants(nxt::ShaderStageBit stage, uint32_t offset, uint32_t count, const void* data) {
|
|
|
|
if (offset + count > kMaxPushConstants) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Setting too many push constants");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetPushConstantsCmd* cmd = allocator.Allocate<SetPushConstantsCmd>(Command::SetPushConstants);
|
|
|
|
new(cmd) SetPushConstantsCmd;
|
|
|
|
cmd->stage = stage;
|
|
|
|
cmd->offset = offset;
|
|
|
|
cmd->count = count;
|
|
|
|
|
|
|
|
uint32_t* values = allocator.AllocateData<uint32_t>(count);
|
|
|
|
memcpy(values, data, count * sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::SetBindGroup(uint32_t groupIndex, BindGroupBase* group) {
|
|
|
|
if (groupIndex >= kMaxBindGroups) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Setting bind group over the max");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SetBindGroupCmd* cmd = allocator.Allocate<SetBindGroupCmd>(Command::SetBindGroup);
|
|
|
|
new(cmd) SetBindGroupCmd;
|
|
|
|
cmd->index = groupIndex;
|
|
|
|
cmd->group = group;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::SetIndexBuffer(BufferBase* buffer, uint32_t offset, nxt::IndexFormat format) {
|
|
|
|
// TODO(kainino@chromium.org): validation
|
|
|
|
|
|
|
|
SetIndexBufferCmd* cmd = allocator.Allocate<SetIndexBufferCmd>(Command::SetIndexBuffer);
|
|
|
|
new(cmd) SetIndexBufferCmd;
|
|
|
|
cmd->buffer = buffer;
|
|
|
|
cmd->offset = offset;
|
|
|
|
cmd->format = format;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::SetVertexBuffers(uint32_t startSlot, uint32_t count, BufferBase* const* buffers, uint32_t const* offsets){
|
|
|
|
// TODO(kainino@chromium.org): validation
|
|
|
|
|
|
|
|
SetVertexBuffersCmd* cmd = allocator.Allocate<SetVertexBuffersCmd>(Command::SetVertexBuffers);
|
|
|
|
new(cmd) SetVertexBuffersCmd;
|
|
|
|
cmd->startSlot = startSlot;
|
|
|
|
cmd->count = count;
|
|
|
|
|
|
|
|
Ref<BufferBase>* cmdBuffers = allocator.AllocateData<Ref<BufferBase>>(count);
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
|
|
new(&cmdBuffers[i]) Ref<BufferBase>(buffers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t* cmdOffsets = allocator.AllocateData<uint32_t>(count);
|
|
|
|
memcpy(cmdOffsets, offsets, count * sizeof(uint32_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::TransitionBufferUsage(BufferBase* buffer, nxt::BufferUsageBit usage) {
|
|
|
|
TransitionBufferUsageCmd* cmd = allocator.Allocate<TransitionBufferUsageCmd>(Command::TransitionBufferUsage);
|
|
|
|
new(cmd) TransitionBufferUsageCmd;
|
|
|
|
cmd->buffer = buffer;
|
|
|
|
cmd->usage = usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::TransitionTextureUsage(TextureBase* texture, nxt::TextureUsageBit usage) {
|
|
|
|
TransitionTextureUsageCmd* cmd = allocator.Allocate<TransitionTextureUsageCmd>(Command::TransitionTextureUsage);
|
|
|
|
new(cmd) TransitionTextureUsageCmd;
|
|
|
|
cmd->texture = texture;
|
|
|
|
cmd->usage = usage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandBufferBuilder::MoveToIterator() {
|
|
|
|
if (!movedToIterator) {
|
|
|
|
iterator = std::move(allocator);
|
|
|
|
movedToIterator = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|