blob: bed17aa1e5c80dadff502b76db96d3772350688b [file] [log] [blame]
// 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::AdvanceSubpass:
{
AdvanceSubpassCmd* cmd = commands->NextCommand<AdvanceSubpassCmd>();
cmd->~AdvanceSubpassCmd();
}
break;
case Command::BeginRenderPass:
{
BeginRenderPassCmd* begin = commands->NextCommand<BeginRenderPassCmd>();
begin->~BeginRenderPassCmd();
}
break;
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::EndRenderPass:
{
EndRenderPassCmd* cmd = commands->NextCommand<EndRenderPassCmd>();
cmd->~EndRenderPassCmd();
}
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();
}
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device) {
}
CommandBufferBuilder::~CommandBufferBuilder() {
if (!commandsAcquired) {
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_RENDER_PASS,
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;
// TODO(kainino@chromium.org): Manage this state inside an object, change lambdas to methods
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 isTextureTransitionPossible = [&](TextureBase* texture, nxt::TextureUsageBit usage) -> bool {
const nxt::TextureUsageBit attachmentUsages =
nxt::TextureUsageBit::ColorAttachment |
nxt::TextureUsageBit::DepthStencilAttachment;
ASSERT(usage != nxt::TextureUsageBit::None && nxt::HasZeroOrOneBits(usage));
if (usage & attachmentUsages) {
return false;
}
auto it = mostRecentTextureUsages.find(texture);
if (it != mostRecentTextureUsages.end()) {
if (it->second & attachmentUsages) {
return false;
}
}
return texture->IsTransitionPossible(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)) {
HandleError("Can't guarantee buffer usage needed by bind group");
return false;
}
}
break;
case nxt::BindingType::SampledTexture:
{
auto requiredUsage = nxt::TextureUsageBit::Sampled;
auto texture = group->GetBindingAsTextureView(i)->GetTexture();
if (!textureHasGuaranteedUsageBit(texture, requiredUsage)) {
HandleError("Can't guarantee texture usage needed by bind group");
return false;
}
}
break;
case nxt::BindingType::Sampler:
continue;
}
}
return true;
};
// TODO(kainino@chromium.org): Manage this state inside an object, change lambda to a method
RenderPassBase* currentRenderPass = nullptr;
FramebufferBase* currentFramebuffer = nullptr;
uint32_t currentSubpass = 0;
auto beginSubpass = [&]() -> bool {
auto& subpassInfo = currentRenderPass->GetSubpassInfo(currentSubpass);
for (auto location : IterateBitSet(subpassInfo.colorAttachmentsSet)) {
auto attachmentSlot = subpassInfo.colorAttachments[location];
auto* tv = currentFramebuffer->GetTextureView(attachmentSlot);
// TODO(kainino@chromium.org): the TextureView can only be null
// because of the null=backbuffer hack (null representing the
// backbuffer). Once that hack is removed (once we have WSI)
// this check isn't needed.
if (tv == nullptr) {
continue;
}
auto* texture = tv->GetTexture();
if (texture->HasFrozenUsage(nxt::TextureUsageBit::ColorAttachment)) {
continue;
}
if (!texture->IsTransitionPossible(nxt::TextureUsageBit::ColorAttachment)) {
HandleError("Can't transition attachment to ColorAttachment usage");
return false;
}
mostRecentTextureUsages[texture] = nxt::TextureUsageBit::ColorAttachment;
texturesTransitioned.insert(texture);
}
return true;
};
auto endSubpass = [&]() {
auto& subpassInfo = currentRenderPass->GetSubpassInfo(currentSubpass);
for (auto location : IterateBitSet(subpassInfo.colorAttachmentsSet)) {
auto attachmentSlot = subpassInfo.colorAttachments[location];
auto* tv = currentFramebuffer->GetTextureView(attachmentSlot);
// TODO(kainino@chromium.org): the TextureView can only be null
// because of the null=backbuffer hack (null representing the
// backbuffer). Once that hack is removed (once we have WSI)
// this check isn't needed.
if (tv == nullptr) {
continue;
}
auto* texture = tv->GetTexture();
if (texture->IsFrozen()) {
continue;
}
mostRecentTextureUsages[texture] = nxt::TextureUsageBit::None;
}
};
Command type;
while(iterator.NextCommandId(&type)) {
switch (type) {
case Command::AdvanceSubpass:
{
iterator.NextCommand<AdvanceSubpassCmd>();
if (currentRenderPass == nullptr) {
HandleError("Can't advance subpass without an active render pass");
return false;
}
if (currentSubpass + 1 >= currentRenderPass->GetSubpassCount()) {
HandleError("Can't advance beyond the last subpass");
return false;
}
endSubpass();
currentSubpass += 1;
if (!beginSubpass()) {
return false;
}
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
}
break;
case Command::BeginRenderPass:
{
BeginRenderPassCmd* begin = iterator.NextCommand<BeginRenderPassCmd>();
auto* renderPass = begin->renderPass.Get();
auto* framebuffer = begin->framebuffer.Get();
if (currentRenderPass != nullptr) {
HandleError("A render pass is already active");
return false;
}
if (!framebuffer->GetRenderPass()->IsCompatibleWith(renderPass)) {
HandleError("Framebuffer is incompatible with this render pass");
return false;
}
aspects.reset(VALIDATION_ASPECT_COMPUTE_PIPELINE);
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
aspects.set(VALIDATION_ASPECT_RENDER_PASS);
currentRenderPass = renderPass;
currentFramebuffer = framebuffer;
currentSubpass = 0;
if (!beginSubpass()) {
return false;
}
}
break;
case Command::CopyBufferToTexture:
{
CopyBufferToTextureCmd* copy = iterator.NextCommand<CopyBufferToTextureCmd>();
BufferBase* buffer = copy->buffer.Get();
uint32_t bufferOffset = copy->bufferOffset;
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 (currentRenderPass) {
HandleError("Blit cannot occur during a render pass");
return false;
}
if (!bufferHasGuaranteedUsageBit(buffer, nxt::BufferUsageBit::TransferSrc)) {
HandleError("Buffer needs the transfer source usage bit");
return false;
}
if (!textureHasGuaranteedUsageBit(texture, nxt::TextureUsageBit::TransferDst)) {
HandleError("Texture needs the transfer destination usage bit");
return false;
}
if (width == 0 || height == 0 || depth == 0) {
HandleError("Empty copy");
return false;
}
// TODO(cwallez@chromium.org): check for overflows
uint64_t pixelSize = TextureFormatPixelSize(texture->GetFormat());
uint64_t dataSize = width * height * depth * pixelSize;
if (dataSize + static_cast<uint64_t>(bufferOffset) > static_cast<uint64_t>(buffer->GetSize())) {
HandleError("Copy would read after end of the buffer");
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()) {
HandleError("Copy would write outside of the texture");
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()) {
HandleError("Some dispatch state is missing");
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()) {
HandleError("Some draw state is missing");
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]) {
HandleError("Draw elements requires an index buffer");
return false;
}
}
}
break;
case Command::EndRenderPass:
{
iterator.NextCommand<EndRenderPassCmd>();
if (currentRenderPass == nullptr) {
HandleError("No render pass is currently active");
return false;
}
if (currentSubpass < currentRenderPass->GetSubpassCount() - 1) {
HandleError("Can't end a render pass before the last subpass");
return false;
}
endSubpass();
currentRenderPass = nullptr;
currentFramebuffer = nullptr;
aspects.reset(VALIDATION_ASPECT_RENDER_PASS);
aspects.reset(VALIDATION_ASPECT_RENDER_PIPELINE);
}
break;
case Command::SetPipeline:
{
SetPipelineCmd* cmd = iterator.NextCommand<SetPipelineCmd>();
PipelineBase* pipeline = cmd->pipeline.Get();
PipelineLayoutBase* layout = pipeline->GetLayout();
if (pipeline->IsCompute()) {
if (currentRenderPass) {
HandleError("Can't use a compute pipeline while a render pass is active");
return false;
}
aspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE);
} else {
if (!currentRenderPass) {
HandleError("A render pass must be active when a render pipeline is set");
return false;
}
if (!pipeline->GetRenderPass()->IsCompatibleWith(currentRenderPass)) {
HandleError("Pipeline is incompatible with this render pass");
return false;
}
aspects.set(VALIDATION_ASPECT_RENDER_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) {
HandleError("Setting pushconstants past the limit");
return false;
}
}
break;
case Command::SetBindGroup:
{
SetBindGroupCmd* cmd = iterator.NextCommand<SetBindGroupCmd>();
uint32_t index = cmd->index;
if (cmd->group->GetLayout() != lastPipeline->GetLayout()->GetBindGroupLayout(index)) {
HandleError("Bind group layout mismatch");
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)) {
HandleError("Buffer needs the index usage bit to be guaranteed");
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)) {
HandleError("Buffer needs vertex usage bit to be guaranteed");
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 (!buffer->IsTransitionPossible(usage)) {
if (buffer->IsFrozen()) {
HandleError("Buffer transition not possible (usage is frozen)");
} else if (!BufferBase::IsUsagePossible(buffer->GetAllowedUsage(), usage)) {
HandleError("Buffer transition not possible (usage not allowed)");
} else {
HandleError("Buffer transition not possible");
}
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 (!isTextureTransitionPossible(texture, usage)) {
if (texture->IsFrozen()) {
HandleError("Texture transition not possible (usage is frozen)");
} else if (!TextureBase::IsUsagePossible(texture->GetAllowedUsage(), usage)) {
HandleError("Texture transition not possible (usage not allowed)");
} else {
HandleError("Texture transition not possible");
}
return false;
}
mostRecentTextureUsages[texture] = usage;
texturesTransitioned.insert(texture);
}
break;
}
}
return true;
}
CommandIterator CommandBufferBuilder::AcquireCommands() {
ASSERT(!commandsAcquired);
commandsAcquired = true;
return std::move(iterator);
}
CommandBufferBase* CommandBufferBuilder::GetResultImpl() {
MoveToIterator();
return device->CreateCommandBuffer(this);
}
void CommandBufferBuilder::AdvanceSubpass() {
allocator.Allocate<AdvanceSubpassCmd>(Command::AdvanceSubpass);
}
void CommandBufferBuilder::BeginRenderPass(RenderPassBase* renderPass, FramebufferBase* framebuffer) {
BeginRenderPassCmd* cmd = allocator.Allocate<BeginRenderPassCmd>(Command::BeginRenderPass);
new(cmd) BeginRenderPassCmd;
cmd->renderPass = renderPass;
cmd->framebuffer = framebuffer;
}
void CommandBufferBuilder::CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
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;
copy->bufferOffset = bufferOffset;
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::EndRenderPass() {
allocator.Allocate<EndRenderPassCmd>(Command::EndRenderPass);
}
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) {
HandleError("Setting too many push constants");
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) {
HandleError("Setting bind group over the max");
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;
}
}
}