Create a command buffer builder state tracker object (#19)
This commit is contained in:
parent
e6e30ecdbf
commit
468dafde9d
|
@ -35,6 +35,8 @@ list(APPEND BACKEND_SOURCES
|
|||
${COMMON_DIR}/CommandBuffer.h
|
||||
${COMMON_DIR}/DepthStencilState.cpp
|
||||
${COMMON_DIR}/DepthStencilState.h
|
||||
${COMMON_DIR}/CommandBufferStateTracker.cpp
|
||||
${COMMON_DIR}/CommandBufferStateTracker.h
|
||||
${COMMON_DIR}/Device.cpp
|
||||
${COMMON_DIR}/Device.h
|
||||
${COMMON_DIR}/Forward.h
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#include "CommandBuffer.h"
|
||||
|
||||
#include "BindGroup.h"
|
||||
#include "BindGroupLayout.h"
|
||||
#include "Buffer.h"
|
||||
#include "Commands.h"
|
||||
#include "CommandBufferStateTracker.h"
|
||||
#include "Device.h"
|
||||
#include "InputState.h"
|
||||
#include "Pipeline.h"
|
||||
|
@ -31,8 +31,8 @@ namespace backend {
|
|||
|
||||
CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder)
|
||||
: device(builder->device),
|
||||
buffersTransitioned(std::move(builder->buffersTransitioned)),
|
||||
texturesTransitioned(std::move(builder->texturesTransitioned)) {
|
||||
buffersTransitioned(std::move(builder->state->buffersTransitioned)),
|
||||
texturesTransitioned(std::move(builder->state->texturesTransitioned)) {
|
||||
}
|
||||
|
||||
bool CommandBufferBase::ValidateResourceUsagesImmediate() {
|
||||
|
@ -156,7 +156,7 @@ namespace backend {
|
|||
commands->DataWasDestroyed();
|
||||
}
|
||||
|
||||
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device) {
|
||||
CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device), state(std::make_unique<CommandBufferStateTracker>(this)) {
|
||||
}
|
||||
|
||||
CommandBufferBuilder::~CommandBufferBuilder() {
|
||||
|
@ -166,212 +166,27 @@ namespace backend {
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
if (!state->AdvanceSubpass()) {
|
||||
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()) {
|
||||
BeginRenderPassCmd* cmd = iterator.NextCommand<BeginRenderPassCmd>();
|
||||
auto* renderPass = cmd->renderPass.Get();
|
||||
auto* framebuffer = cmd->framebuffer.Get();
|
||||
if (!state->BeginRenderPass(renderPass, framebuffer)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -391,21 +206,6 @@ namespace backend {
|
|||
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;
|
||||
|
@ -414,7 +214,6 @@ namespace backend {
|
|||
// 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;
|
||||
|
@ -427,88 +226,48 @@ namespace backend {
|
|||
HandleError("Copy would write outside of the texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state->ValidateCanCopy() ||
|
||||
!state->ValidateCanUseBufferAs(buffer, nxt::BufferUsageBit::TransferSrc) ||
|
||||
!state->ValidateCanUseTextureAs(texture, nxt::TextureUsageBit::TransferDst)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::Dispatch:
|
||||
{
|
||||
DispatchCmd* cmd = iterator.NextCommand<DispatchCmd>();
|
||||
|
||||
constexpr ValidationAspects requiredDispatchAspects =
|
||||
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE |
|
||||
1 << VALIDATION_ASPECT_BINDGROUPS;
|
||||
|
||||
if ((requiredDispatchAspects & ~aspects).any()) {
|
||||
// Compute the lazily computed aspects
|
||||
if (bindgroupsSet.all()) {
|
||||
aspects.set(VALIDATION_ASPECT_BINDGROUPS);
|
||||
}
|
||||
|
||||
// Check again if anything is missing
|
||||
if ((requiredDispatchAspects & ~aspects).any()) {
|
||||
HandleError("Some dispatch state is missing");
|
||||
iterator.NextCommand<DispatchCmd>();
|
||||
if (!state->ValidateCanDispatch()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Command::DrawArrays:
|
||||
{
|
||||
iterator.NextCommand<DrawArraysCmd>();
|
||||
if (!state->ValidateCanDrawArrays()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
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");
|
||||
iterator.NextCommand<DrawElementsCmd>();
|
||||
if (!state->ValidateCanDrawElements()) {
|
||||
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");
|
||||
if (!state->EndRenderPass()) {
|
||||
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;
|
||||
|
||||
|
@ -516,40 +275,9 @@ namespace backend {
|
|||
{
|
||||
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");
|
||||
if (!state->SetPipeline(pipeline)) {
|
||||
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;
|
||||
|
||||
|
@ -567,7 +295,7 @@ namespace backend {
|
|||
case Command::SetStencilReference:
|
||||
{
|
||||
SetStencilReferenceCmd* cmd = iterator.NextCommand<SetStencilReferenceCmd>();
|
||||
if (currentRenderPass == nullptr) {
|
||||
if (!state->HaveRenderPass()) {
|
||||
HandleError("Can't set stencil reference without an active render pass");
|
||||
return false;
|
||||
}
|
||||
|
@ -577,30 +305,18 @@ namespace backend {
|
|||
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");
|
||||
if (!state->SetBindGroup(cmd->index, cmd->group.Get())) {
|
||||
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");
|
||||
if (!state->SetIndexBuffer(cmd->buffer.Get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -611,13 +327,7 @@ namespace backend {
|
|||
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);
|
||||
state->SetVertexBuffer(cmd->startSlot + i, buffers[i].Get());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -625,46 +335,19 @@ namespace backend {
|
|||
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");
|
||||
}
|
||||
if (!state->TransitionBufferUsage(cmd->buffer.Get(), cmd->usage)) {
|
||||
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");
|
||||
}
|
||||
if (!state->TransitionTextureUsage(cmd->texture.Get(), cmd->usage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mostRecentTextureUsages[texture] = usage;
|
||||
|
||||
texturesTransitioned.insert(texture);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef BACKEND_COMMON_COMMANDBUFFERGL_H_
|
||||
#define BACKEND_COMMON_COMMANDBUFFERGL_H_
|
||||
#ifndef BACKEND_COMMON_COMMANDBUFFER_H_
|
||||
#define BACKEND_COMMON_COMMANDBUFFER_H_
|
||||
|
||||
#include "nxt/nxtcpp.h"
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
#include "Builder.h"
|
||||
#include "RefCounted.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
|
@ -28,6 +29,7 @@ namespace backend {
|
|||
|
||||
class BindGroupBase;
|
||||
class BufferBase;
|
||||
class CommandBufferStateTracker;
|
||||
class FramebufferBase;
|
||||
class DeviceBase;
|
||||
class PipelineBase;
|
||||
|
@ -88,16 +90,13 @@ namespace backend {
|
|||
CommandBufferBase* GetResultImpl() override;
|
||||
void MoveToIterator();
|
||||
|
||||
std::unique_ptr<CommandBufferStateTracker> state;
|
||||
CommandAllocator allocator;
|
||||
CommandIterator iterator;
|
||||
bool movedToIterator = false;
|
||||
bool commandsAcquired = false;
|
||||
// These pointers will remain valid since they are referenced by
|
||||
// the bind groups which are referenced by this command buffer.
|
||||
std::set<BufferBase*> buffersTransitioned;
|
||||
std::set<TextureBase*> texturesTransitioned;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BACKEND_COMMON_COMMANDBUFFERGL_H_
|
||||
#endif // BACKEND_COMMON_COMMANDBUFFER_H_
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
// 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 "CommandBufferStateTracker.h"
|
||||
|
||||
#include "Forward.h"
|
||||
#include "BindGroup.h"
|
||||
#include "BindGroupLayout.h"
|
||||
#include "BitSetIterator.h"
|
||||
#include "Buffer.h"
|
||||
#include "Framebuffer.h"
|
||||
#include "InputState.h"
|
||||
#include "Pipeline.h"
|
||||
#include "PipelineLayout.h"
|
||||
#include "RenderPass.h"
|
||||
#include "Texture.h"
|
||||
|
||||
namespace backend {
|
||||
CommandBufferStateTracker::CommandBufferStateTracker(CommandBufferBuilder* builder)
|
||||
: builder(builder) {
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::HaveRenderPass() const {
|
||||
return currentRenderPass != nullptr;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanCopy() const {
|
||||
if (currentRenderPass) {
|
||||
builder->HandleError("Copy cannot occur during a render pass");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanUseBufferAs(BufferBase* buffer, nxt::BufferUsageBit usage) const {
|
||||
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
||||
builder->HandleError("Buffer is not in the necessary usage");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanUseTextureAs(TextureBase* texture, nxt::TextureUsageBit usage) const {
|
||||
if (!TextureHasGuaranteedUsageBit(texture, usage)) {
|
||||
builder->HandleError("Texture is not in the necessary usage");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanDispatch() {
|
||||
constexpr ValidationAspects requiredAspects =
|
||||
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE |
|
||||
1 << VALIDATION_ASPECT_BIND_GROUPS;
|
||||
if ((requiredAspects & ~aspects).none()) {
|
||||
// Fast return-true path if everything is good
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aspects[VALIDATION_ASPECT_COMPUTE_PIPELINE]) {
|
||||
builder->HandleError("No active compute pipeline");
|
||||
return false;
|
||||
}
|
||||
// Compute the lazily computed aspects
|
||||
if (!RecomputeHaveAspectBindGroups()) {
|
||||
builder->HandleError("Some bind groups are not set");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanDrawArrays() {
|
||||
// TODO(kainino@chromium.org): Check for a current render pass
|
||||
constexpr ValidationAspects requiredAspects =
|
||||
1 << VALIDATION_ASPECT_RENDER_PIPELINE |
|
||||
1 << VALIDATION_ASPECT_BIND_GROUPS |
|
||||
1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
|
||||
if ((requiredAspects & ~aspects).none()) {
|
||||
// Fast return-true path if everything is good
|
||||
return true;
|
||||
}
|
||||
|
||||
return RevalidateCanDraw();
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateCanDrawElements() {
|
||||
// TODO(kainino@chromium.org): Check for a current render pass
|
||||
constexpr ValidationAspects requiredAspects =
|
||||
1 << VALIDATION_ASPECT_RENDER_PIPELINE |
|
||||
1 << VALIDATION_ASPECT_BIND_GROUPS |
|
||||
1 << VALIDATION_ASPECT_VERTEX_BUFFERS |
|
||||
1 << VALIDATION_ASPECT_INDEX_BUFFER;
|
||||
if ((requiredAspects & ~aspects).none()) {
|
||||
// Fast return-true path if everything is good
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
|
||||
builder->HandleError("Cannot DrawElements without index buffer set");
|
||||
return false;
|
||||
}
|
||||
return RevalidateCanDraw();
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::BeginSubpass() {
|
||||
if (currentRenderPass == nullptr) {
|
||||
builder->HandleError("Can't begin a subpass without an active render pass");
|
||||
return false;
|
||||
}
|
||||
if (subpassActive) {
|
||||
builder->HandleError("Can't begin a subpass without ending the previous subpass");
|
||||
return false;
|
||||
}
|
||||
if (currentSubpass >= currentRenderPass->GetSubpassCount()) {
|
||||
builder->HandleError("Can't begin a subpass beyond the last subpass");
|
||||
return false;
|
||||
}
|
||||
|
||||
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)) {
|
||||
builder->HandleError("Can't transition attachment to ColorAttachment usage");
|
||||
return false;
|
||||
}
|
||||
mostRecentTextureUsages[texture] = nxt::TextureUsageBit::ColorAttachment;
|
||||
texturesTransitioned.insert(texture);
|
||||
}
|
||||
|
||||
subpassActive = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::EndSubpass() {
|
||||
if (currentRenderPass == nullptr) {
|
||||
builder->HandleError("Can't end a subpass without an active render pass");
|
||||
return false;
|
||||
}
|
||||
if (!subpassActive) {
|
||||
builder->HandleError("Can't end a subpass without beginning one");
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
currentSubpass += 1;
|
||||
subpassActive = false;
|
||||
UnsetPipeline();
|
||||
return true;
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::BeginRenderPass(RenderPassBase* renderPass, FramebufferBase* framebuffer) {
|
||||
if (currentRenderPass != nullptr) {
|
||||
builder->HandleError("A render pass is already active");
|
||||
return false;
|
||||
}
|
||||
if (!framebuffer->GetRenderPass()->IsCompatibleWith(renderPass)) {
|
||||
builder->HandleError("Framebuffer is incompatible with this render pass");
|
||||
return false;
|
||||
}
|
||||
|
||||
currentRenderPass = renderPass;
|
||||
currentFramebuffer = framebuffer;
|
||||
currentSubpass = 0;
|
||||
subpassActive = false;
|
||||
|
||||
// TODO(kainino@chromium.org): remove this when AdvanceSubpass is removed.
|
||||
if (!BeginSubpass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UnsetPipeline();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::AdvanceSubpass() {
|
||||
// TODO(kainino@chromium.org): remove this function when AdvanceSubpass is removed.
|
||||
return EndSubpass() && BeginSubpass();
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::EndRenderPass() {
|
||||
if (currentRenderPass == nullptr) {
|
||||
builder->HandleError("No render pass is currently active");
|
||||
return false;
|
||||
}
|
||||
// TODO(kainino@chromium.org): remove this when AdvanceSubpass is removed.
|
||||
if (!EndSubpass()) {
|
||||
return false;
|
||||
}
|
||||
if (subpassActive) {
|
||||
builder->HandleError("Can't end a render pass while a subpass is active");
|
||||
return false;
|
||||
}
|
||||
if (currentSubpass < currentRenderPass->GetSubpassCount() - 1) {
|
||||
builder->HandleError("Can't end a render pass before the last subpass");
|
||||
return false;
|
||||
}
|
||||
currentRenderPass = nullptr;
|
||||
currentFramebuffer = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::SetPipeline(PipelineBase* pipeline) {
|
||||
PipelineLayoutBase* layout = pipeline->GetLayout();
|
||||
|
||||
if (pipeline->IsCompute()) {
|
||||
if (currentRenderPass) {
|
||||
builder->HandleError("Can't use a compute pipeline while a render pass is active");
|
||||
return false;
|
||||
}
|
||||
aspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE);
|
||||
} else {
|
||||
if (!currentRenderPass) {
|
||||
builder->HandleError("A render pass must be active when a render pipeline is set");
|
||||
return false;
|
||||
}
|
||||
if (!pipeline->GetRenderPass()->IsCompatibleWith(currentRenderPass)) {
|
||||
builder->HandleError("Pipeline is incompatible with this render pass");
|
||||
return false;
|
||||
}
|
||||
aspects.set(VALIDATION_ASPECT_RENDER_PIPELINE);
|
||||
}
|
||||
aspects.reset(VALIDATION_ASPECT_BIND_GROUPS);
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) {
|
||||
if (bindgroup->GetLayout() != lastPipeline->GetLayout()->GetBindGroupLayout(index)) {
|
||||
builder->HandleError("Bind group layout mismatch");
|
||||
return false;
|
||||
}
|
||||
if (!ValidateBindGroupUsages(bindgroup)) {
|
||||
return false;
|
||||
}
|
||||
bindgroupsSet.set(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::SetIndexBuffer(BufferBase* buffer) {
|
||||
if (!HavePipeline()) {
|
||||
builder->HandleError("Can't set the index buffer without a pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto usage = nxt::BufferUsageBit::Index;
|
||||
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
||||
builder->HandleError("Buffer needs the index usage bit to be guaranteed");
|
||||
return false;
|
||||
}
|
||||
|
||||
aspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::SetVertexBuffer(uint32_t index, BufferBase* buffer) {
|
||||
if (!HavePipeline()) {
|
||||
builder->HandleError("Can't set vertex buffers without a pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto usage = nxt::BufferUsageBit::Vertex;
|
||||
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
||||
builder->HandleError("Buffer needs vertex usage bit to be guaranteed");
|
||||
return false;
|
||||
}
|
||||
|
||||
inputsSet.set(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::TransitionBufferUsage(BufferBase* buffer, nxt::BufferUsageBit usage) {
|
||||
if (!buffer->IsTransitionPossible(usage)) {
|
||||
if (buffer->IsFrozen()) {
|
||||
builder->HandleError("Buffer transition not possible (usage is frozen)");
|
||||
} else if (!BufferBase::IsUsagePossible(buffer->GetAllowedUsage(), usage)) {
|
||||
builder->HandleError("Buffer transition not possible (usage not allowed)");
|
||||
} else {
|
||||
builder->HandleError("Buffer transition not possible");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mostRecentBufferUsages[buffer] = usage;
|
||||
buffersTransitioned.insert(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::TransitionTextureUsage(TextureBase* texture, nxt::TextureUsageBit usage) {
|
||||
if (!IsTextureTransitionPossible(texture, usage)) {
|
||||
if (texture->IsFrozen()) {
|
||||
builder->HandleError("Texture transition not possible (usage is frozen)");
|
||||
} else if (!TextureBase::IsUsagePossible(texture->GetAllowedUsage(), usage)) {
|
||||
builder->HandleError("Texture transition not possible (usage not allowed)");
|
||||
} else {
|
||||
builder->HandleError("Texture transition not possible");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mostRecentTextureUsages[texture] = usage;
|
||||
texturesTransitioned.insert(texture);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::BufferHasGuaranteedUsageBit(BufferBase* buffer, nxt::BufferUsageBit usage) const {
|
||||
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);
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::TextureHasGuaranteedUsageBit(TextureBase* texture, nxt::TextureUsageBit usage) const {
|
||||
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);
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::IsTextureTransitionPossible(TextureBase* texture, nxt::TextureUsageBit usage) const {
|
||||
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);
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() {
|
||||
if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
|
||||
return true;
|
||||
}
|
||||
if (bindgroupsSet.all()) {
|
||||
aspects.set(VALIDATION_ASPECT_BIND_GROUPS);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::RecomputeHaveAspectVertexBuffers() {
|
||||
if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
|
||||
return true;
|
||||
}
|
||||
auto requiredInputs = lastPipeline->GetInputState()->GetInputsSetMask();
|
||||
if ((inputsSet & ~requiredInputs).none()) {
|
||||
aspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::HavePipeline() const {
|
||||
constexpr ValidationAspects pipelineAspects =
|
||||
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE |
|
||||
1 << VALIDATION_ASPECT_RENDER_PIPELINE;
|
||||
return (aspects & pipelineAspects).any();
|
||||
}
|
||||
|
||||
bool CommandBufferStateTracker::ValidateBindGroupUsages(BindGroupBase* group) const {
|
||||
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)) {
|
||||
builder->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)) {
|
||||
builder->HandleError("Can't guarantee texture usage needed by bind group");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case nxt::BindingType::Sampler:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool CommandBufferStateTracker::RevalidateCanDraw() {
|
||||
if (!aspects[VALIDATION_ASPECT_RENDER_PIPELINE]) {
|
||||
builder->HandleError("No active render pipeline");
|
||||
return false;
|
||||
}
|
||||
// Compute the lazily computed aspects
|
||||
if (!RecomputeHaveAspectBindGroups()) {
|
||||
builder->HandleError("Some bind groups are not set");
|
||||
return false;
|
||||
}
|
||||
if (!RecomputeHaveAspectVertexBuffers()) {
|
||||
builder->HandleError("Some vertex buffers are not set");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommandBufferStateTracker::UnsetPipeline() {
|
||||
// All of the aspects (currently) are pipeline-dependent.
|
||||
aspects.reset();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
// 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.
|
||||
|
||||
#ifndef BACKEND_COMMON_COMMANDBUFFERSTATETRACKER_H
|
||||
#define BACKEND_COMMON_COMMANDBUFFERSTATETRACKER_H
|
||||
|
||||
#include "CommandBuffer.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace backend {
|
||||
class CommandBufferStateTracker {
|
||||
public:
|
||||
explicit CommandBufferStateTracker(CommandBufferBuilder* builder);
|
||||
|
||||
// Non-state-modifying validation functions
|
||||
bool HaveRenderPass() const;
|
||||
bool ValidateCanCopy() const;
|
||||
bool ValidateCanUseBufferAs(BufferBase* buffer, nxt::BufferUsageBit usage) const;
|
||||
bool ValidateCanUseTextureAs(TextureBase* texture, nxt::TextureUsageBit usage) const;
|
||||
bool ValidateCanDispatch();
|
||||
bool ValidateCanDrawArrays();
|
||||
bool ValidateCanDrawElements();
|
||||
|
||||
// State-modifying methods
|
||||
bool BeginSubpass();
|
||||
bool EndSubpass();
|
||||
bool BeginRenderPass(RenderPassBase* renderPass, FramebufferBase* framebuffer);
|
||||
bool AdvanceSubpass();
|
||||
bool EndRenderPass();
|
||||
bool SetPipeline(PipelineBase* pipeline);
|
||||
bool SetBindGroup(uint32_t index, BindGroupBase* bindgroup);
|
||||
bool SetIndexBuffer(BufferBase* buffer);
|
||||
bool SetVertexBuffer(uint32_t index, BufferBase* buffer);
|
||||
bool TransitionBufferUsage(BufferBase* buffer, nxt::BufferUsageBit usage);
|
||||
bool TransitionTextureUsage(TextureBase* texture, nxt::TextureUsageBit usage);
|
||||
|
||||
// These collections are copied to the CommandBuffer at build time.
|
||||
// These pointers will remain valid since they are referenced by
|
||||
// the bind groups which are referenced by this command buffer.
|
||||
std::set<BufferBase*> buffersTransitioned;
|
||||
std::set<TextureBase*> texturesTransitioned;
|
||||
|
||||
private:
|
||||
enum ValidationAspect {
|
||||
VALIDATION_ASPECT_RENDER_PIPELINE,
|
||||
VALIDATION_ASPECT_COMPUTE_PIPELINE,
|
||||
VALIDATION_ASPECT_BIND_GROUPS,
|
||||
VALIDATION_ASPECT_VERTEX_BUFFERS,
|
||||
VALIDATION_ASPECT_INDEX_BUFFER,
|
||||
|
||||
VALIDATION_ASPECT_COUNT
|
||||
};
|
||||
using ValidationAspects = std::bitset<VALIDATION_ASPECT_COUNT>;
|
||||
|
||||
// Usage helper functions
|
||||
bool BufferHasGuaranteedUsageBit(BufferBase* buffer, nxt::BufferUsageBit usage) const;
|
||||
bool TextureHasGuaranteedUsageBit(TextureBase* texture, nxt::TextureUsageBit usage) const;
|
||||
bool IsTextureTransitionPossible(TextureBase* texture, nxt::TextureUsageBit usage) const;
|
||||
|
||||
// Queries for lazily evaluated aspects
|
||||
bool RecomputeHaveAspectBindGroups();
|
||||
bool RecomputeHaveAspectVertexBuffers();
|
||||
|
||||
bool HavePipeline() const;
|
||||
bool ValidateBindGroupUsages(BindGroupBase* group) const;
|
||||
bool RevalidateCanDraw();
|
||||
|
||||
void UnsetPipeline();
|
||||
|
||||
CommandBufferBuilder* builder;
|
||||
|
||||
ValidationAspects aspects;
|
||||
|
||||
std::bitset<kMaxBindGroups> bindgroupsSet;
|
||||
std::bitset<kMaxVertexInputs> inputsSet;
|
||||
PipelineBase* lastPipeline = nullptr;
|
||||
|
||||
std::map<BufferBase*, nxt::BufferUsageBit> mostRecentBufferUsages;
|
||||
std::map<TextureBase*, nxt::TextureUsageBit> mostRecentTextureUsages;
|
||||
|
||||
RenderPassBase* currentRenderPass = nullptr;
|
||||
FramebufferBase* currentFramebuffer = nullptr;
|
||||
bool subpassActive = false;
|
||||
uint32_t currentSubpass = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BACKEND_COMMON_COMMANDBUFFERSTATETRACKER_H
|
Loading…
Reference in New Issue