2017-06-09 00:25:57 +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.
|
|
|
|
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/CommandBufferStateTracker.h"
|
2017-06-09 00:25:57 +00:00
|
|
|
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/BindGroup.h"
|
|
|
|
#include "backend/BindGroupLayout.h"
|
|
|
|
#include "backend/Buffer.h"
|
2017-07-14 14:58:07 +00:00
|
|
|
#include "backend/ComputePipeline.h"
|
2017-11-24 18:59:42 +00:00
|
|
|
#include "backend/Forward.h"
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/InputState.h"
|
|
|
|
#include "backend/PipelineLayout.h"
|
2018-05-11 17:04:44 +00:00
|
|
|
#include "backend/RenderPassDescriptor.h"
|
2017-07-14 14:58:07 +00:00
|
|
|
#include "backend/RenderPipeline.h"
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/Texture.h"
|
2017-07-10 17:46:05 +00:00
|
|
|
#include "common/Assert.h"
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "common/BitSetIterator.h"
|
2017-06-09 00:25:57 +00:00
|
|
|
|
|
|
|
namespace backend {
|
2017-11-23 18:32:51 +00:00
|
|
|
CommandBufferStateTracker::CommandBufferStateTracker(CommandBufferBuilder* mBuilder)
|
|
|
|
: mBuilder(mBuilder) {
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::HaveRenderPass() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mCurrentRenderPass != nullptr;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::ValidateCanCopy() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mCurrentRenderPass) {
|
|
|
|
mBuilder->HandleError("Copy cannot occur during a render pass");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::ValidateCanUseBufferAs(BufferBase* buffer,
|
|
|
|
nxt::BufferUsageBit usage) const {
|
2017-06-09 00:25:57 +00:00
|
|
|
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer is not in the necessary usage");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::ValidateCanUseTextureAs(TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) const {
|
2017-06-09 00:25:57 +00:00
|
|
|
if (!TextureHasGuaranteedUsageBit(texture, usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Texture is not in the necessary usage");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::ValidateCanDispatch() {
|
|
|
|
constexpr ValidationAspects requiredAspects =
|
2017-11-24 18:59:42 +00:00
|
|
|
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE | // implicitly requires COMPUTE_PASS
|
2017-06-09 00:25:57 +00:00
|
|
|
1 << VALIDATION_ASPECT_BIND_GROUPS;
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((requiredAspects & ~mAspects).none()) {
|
2017-06-09 00:25:57 +00:00
|
|
|
// Fast return-true path if everything is good
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_COMPUTE_PIPELINE]) {
|
|
|
|
mBuilder->HandleError("No active compute pipeline");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
// Compute the lazily computed mAspects
|
2017-06-09 00:25:57 +00:00
|
|
|
if (!RecomputeHaveAspectBindGroups()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Bind group state not valid");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::ValidateCanDrawArrays() {
|
|
|
|
// TODO(kainino@chromium.org): Check for a current render pass
|
|
|
|
constexpr ValidationAspects requiredAspects =
|
2018-05-02 22:10:13 +00:00
|
|
|
1 << VALIDATION_ASPECT_RENDER_PIPELINE | // implicitly requires RENDER_PASS
|
2017-11-24 18:59:42 +00:00
|
|
|
1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((requiredAspects & ~mAspects).none()) {
|
2017-06-09 00:25:57 +00:00
|
|
|
// 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 =
|
2017-11-24 18:59:42 +00:00
|
|
|
1 << VALIDATION_ASPECT_RENDER_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
|
|
|
|
1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((requiredAspects & ~mAspects).none()) {
|
2017-06-09 00:25:57 +00:00
|
|
|
// Fast return-true path if everything is good
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
|
|
|
|
mBuilder->HandleError("Cannot DrawElements without index buffer set");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return RevalidateCanDraw();
|
|
|
|
}
|
|
|
|
|
2017-06-09 01:25:14 +00:00
|
|
|
bool CommandBufferStateTracker::ValidateEndCommandBuffer() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mCurrentRenderPass != nullptr) {
|
|
|
|
mBuilder->HandleError("Can't end command buffer with an active render pass");
|
2017-06-09 01:25:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) {
|
|
|
|
mBuilder->HandleError("Can't end command buffer with an active compute pass");
|
2017-07-10 21:07:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-20 15:01:37 +00:00
|
|
|
bool CommandBufferStateTracker::ValidateSetPushConstants(nxt::ShaderStageBit stages) {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) {
|
2017-07-20 15:01:37 +00:00
|
|
|
if (stages & ~nxt::ShaderStageBit::Compute) {
|
2017-11-24 18:59:42 +00:00
|
|
|
mBuilder->HandleError(
|
|
|
|
"SetPushConstants stage must be compute or 0 in compute passes");
|
2017-07-20 15:01:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-05-02 22:10:13 +00:00
|
|
|
} else if (mAspects[VALIDATION_ASPECT_RENDER_PASS]) {
|
2017-07-20 15:01:37 +00:00
|
|
|
if (stages & ~(nxt::ShaderStageBit::Vertex | nxt::ShaderStageBit::Fragment)) {
|
2017-11-24 18:59:42 +00:00
|
|
|
mBuilder->HandleError(
|
2018-05-02 22:10:13 +00:00
|
|
|
"SetPushConstants stage must be a subset if (vertex|fragment) in render "
|
|
|
|
"passes");
|
2017-07-20 15:01:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
2017-11-24 18:59:42 +00:00
|
|
|
mBuilder->HandleError(
|
2018-05-02 22:10:13 +00:00
|
|
|
"PushConstants must be set in either compute passes or render passes");
|
2017-07-20 15:01:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-10 21:07:24 +00:00
|
|
|
bool CommandBufferStateTracker::BeginComputePass() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mCurrentRenderPass != nullptr) {
|
|
|
|
mBuilder->HandleError("Cannot begin a compute pass while a render pass is active");
|
2017-07-10 21:07:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.set(VALIDATION_ASPECT_COMPUTE_PASS);
|
2017-07-10 21:07:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::EndComputePass() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) {
|
|
|
|
mBuilder->HandleError("Can't end a compute pass without beginning one");
|
2017-07-10 21:07:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.reset(VALIDATION_ASPECT_COMPUTE_PASS);
|
2017-07-10 21:07:24 +00:00
|
|
|
UnsetPipeline();
|
2017-06-09 01:25:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-11 17:04:44 +00:00
|
|
|
bool CommandBufferStateTracker::BeginRenderPass(RenderPassDescriptorBase* info) {
|
2018-05-02 22:10:13 +00:00
|
|
|
if (mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) {
|
|
|
|
mBuilder->HandleError("Cannot begin a render pass while a compute pass is active");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-05-02 22:10:13 +00:00
|
|
|
if (mCurrentRenderPass != nullptr) {
|
|
|
|
mBuilder->HandleError("A render pass is already active");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-02 22:10:13 +00:00
|
|
|
mCurrentRenderPass = info;
|
|
|
|
mAspects.set(VALIDATION_ASPECT_RENDER_PASS);
|
|
|
|
|
|
|
|
for (uint32_t i : IterateBitSet(info->GetColorAttachmentMask())) {
|
|
|
|
TextureBase* texture = info->GetColorAttachment(i).view->GetTexture();
|
2017-07-07 23:06:14 +00:00
|
|
|
if (!EnsureTextureUsage(texture, nxt::TextureUsageBit::OutputAttachment)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Unable to ensure texture has OutputAttachment usage");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mTexturesAttached.insert(texture);
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-02 22:10:13 +00:00
|
|
|
if (info->HasDepthStencilAttachment()) {
|
|
|
|
TextureBase* texture = info->GetDepthStencilAttachment().view->GetTexture();
|
|
|
|
if (!EnsureTextureUsage(texture, nxt::TextureUsageBit::OutputAttachment)) {
|
|
|
|
mBuilder->HandleError("Unable to ensure texture has OutputAttachment usage");
|
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2018-05-02 22:10:13 +00:00
|
|
|
mTexturesAttached.insert(texture);
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::EndRenderPass() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mCurrentRenderPass == nullptr) {
|
|
|
|
mBuilder->HandleError("No render pass is currently active");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-05-02 22:10:13 +00:00
|
|
|
|
|
|
|
// Everything in mTexturesAttached should be for the current render pass.
|
|
|
|
mTexturesAttached.clear();
|
|
|
|
|
|
|
|
mInputsSet.reset();
|
|
|
|
UnsetPipeline();
|
|
|
|
|
|
|
|
mAspects.reset(VALIDATION_ASPECT_RENDER_PASS);
|
2017-11-23 18:32:51 +00:00
|
|
|
mCurrentRenderPass = nullptr;
|
2017-06-09 00:25:57 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-14 14:58:07 +00:00
|
|
|
bool CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_COMPUTE_PASS]) {
|
|
|
|
mBuilder->HandleError("A compute pass must be active when a compute pipeline is set");
|
2017-07-14 14:58:07 +00:00
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mCurrentRenderPass) {
|
|
|
|
mBuilder->HandleError("Can't use a compute pipeline while a render pass is active");
|
2017-07-14 14:58:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-08-31 17:56:15 +00:00
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.set(VALIDATION_ASPECT_COMPUTE_PIPELINE);
|
2017-08-31 17:56:15 +00:00
|
|
|
SetPipelineCommon(pipeline);
|
|
|
|
return true;
|
2017-07-14 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::SetRenderPipeline(RenderPipelineBase* pipeline) {
|
2018-05-02 22:10:13 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_RENDER_PASS]) {
|
|
|
|
mBuilder->HandleError("A render pass must be active when a render pipeline is set");
|
2017-07-14 14:58:07 +00:00
|
|
|
return false;
|
|
|
|
}
|
2018-05-02 22:10:13 +00:00
|
|
|
if (!pipeline->IsCompatibleWith(mCurrentRenderPass)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Pipeline is incompatible with this render pass");
|
2017-07-14 14:58:07 +00:00
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-08-31 17:56:15 +00:00
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.set(VALIDATION_ASPECT_RENDER_PIPELINE);
|
|
|
|
mLastRenderPipeline = pipeline;
|
2017-08-31 17:56:15 +00:00
|
|
|
SetPipelineCommon(pipeline);
|
|
|
|
return true;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) {
|
|
|
|
if (!ValidateBindGroupUsages(bindgroup)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mBindgroupsSet.set(index);
|
|
|
|
mBindgroups[index] = bindgroup;
|
2017-06-09 00:25:57 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::SetIndexBuffer(BufferBase* buffer) {
|
|
|
|
if (!HavePipeline()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Can't set the index buffer without a pipeline");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto usage = nxt::BufferUsageBit::Index;
|
|
|
|
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer needs the index usage bit to be guaranteed");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::SetVertexBuffer(uint32_t index, BufferBase* buffer) {
|
|
|
|
if (!HavePipeline()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Can't set vertex buffers without a pipeline");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto usage = nxt::BufferUsageBit::Vertex;
|
|
|
|
if (!BufferHasGuaranteedUsageBit(buffer, usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer needs vertex usage bit to be guaranteed");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mInputsSet.set(index);
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::TransitionBufferUsage(BufferBase* buffer,
|
|
|
|
nxt::BufferUsageBit usage) {
|
2017-06-09 00:25:57 +00:00
|
|
|
if (!buffer->IsTransitionPossible(usage)) {
|
|
|
|
if (buffer->IsFrozen()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer transition not possible (usage is frozen)");
|
2017-06-09 00:25:57 +00:00
|
|
|
} else if (!BufferBase::IsUsagePossible(buffer->GetAllowedUsage(), usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer transition not possible (usage not allowed)");
|
2017-06-09 00:25:57 +00:00
|
|
|
} else {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Buffer transition not possible");
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mMostRecentBufferUsages[buffer] = usage;
|
|
|
|
mBuffersTransitioned.insert(buffer);
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::TransitionTextureUsage(TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) {
|
2017-07-07 23:06:14 +00:00
|
|
|
if (!IsExplicitTextureTransitionPossible(texture, usage)) {
|
2017-06-09 00:25:57 +00:00
|
|
|
if (texture->IsFrozen()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Texture transition not possible (usage is frozen)");
|
2017-06-09 00:25:57 +00:00
|
|
|
} else if (!TextureBase::IsUsagePossible(texture->GetAllowedUsage(), usage)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Texture transition not possible (usage not allowed)");
|
|
|
|
} else if (mTexturesAttached.find(texture) != mTexturesAttached.end()) {
|
2017-11-24 18:59:42 +00:00
|
|
|
mBuilder->HandleError(
|
|
|
|
"Texture transition not possible (texture is in use as a framebuffer "
|
|
|
|
"attachment)");
|
2017-06-09 00:25:57 +00:00
|
|
|
} else {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Texture transition not possible");
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mMostRecentTextureUsages[texture] = usage;
|
|
|
|
mTexturesTransitioned.insert(texture);
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::EnsureTextureUsage(TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) {
|
2017-08-14 20:15:39 +00:00
|
|
|
if (texture->HasFrozenUsage(usage)) {
|
2017-07-07 23:06:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-07-10 20:50:36 +00:00
|
|
|
if (!IsInternalTextureTransitionPossible(texture, usage)) {
|
2017-07-07 23:06:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mMostRecentTextureUsages[texture] = usage;
|
|
|
|
mTexturesTransitioned.insert(texture);
|
2017-07-07 23:06:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::BufferHasGuaranteedUsageBit(BufferBase* buffer,
|
|
|
|
nxt::BufferUsageBit usage) const {
|
2017-06-09 00:25:57 +00:00
|
|
|
ASSERT(usage != nxt::BufferUsageBit::None && nxt::HasZeroOrOneBits(usage));
|
|
|
|
if (buffer->HasFrozenUsage(usage)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
auto it = mMostRecentBufferUsages.find(buffer);
|
|
|
|
return it != mMostRecentBufferUsages.end() && (it->second & usage);
|
2017-07-22 00:00:22 +00:00
|
|
|
}
|
2017-06-09 00:25:57 +00:00
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::TextureHasGuaranteedUsageBit(TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) const {
|
2017-06-09 00:25:57 +00:00
|
|
|
ASSERT(usage != nxt::TextureUsageBit::None && nxt::HasZeroOrOneBits(usage));
|
|
|
|
if (texture->HasFrozenUsage(usage)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
auto it = mMostRecentTextureUsages.find(texture);
|
|
|
|
return it != mMostRecentTextureUsages.end() && (it->second & usage);
|
2017-07-22 00:00:22 +00:00
|
|
|
}
|
2017-06-09 00:25:57 +00:00
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::IsInternalTextureTransitionPossible(
|
|
|
|
TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) const {
|
2017-06-09 00:25:57 +00:00
|
|
|
ASSERT(usage != nxt::TextureUsageBit::None && nxt::HasZeroOrOneBits(usage));
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mTexturesAttached.find(texture) != mTexturesAttached.end()) {
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return texture->IsTransitionPossible(usage);
|
2017-07-22 00:00:22 +00:00
|
|
|
}
|
2017-06-09 00:25:57 +00:00
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool CommandBufferStateTracker::IsExplicitTextureTransitionPossible(
|
|
|
|
TextureBase* texture,
|
|
|
|
nxt::TextureUsageBit usage) const {
|
|
|
|
const nxt::TextureUsageBit attachmentUsages = nxt::TextureUsageBit::OutputAttachment;
|
2017-07-07 23:06:14 +00:00
|
|
|
if (usage & attachmentUsages) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return IsInternalTextureTransitionPossible(texture, usage);
|
|
|
|
}
|
|
|
|
|
2017-06-09 00:25:57 +00:00
|
|
|
bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mAspects[VALIDATION_ASPECT_BIND_GROUPS]) {
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-07-07 23:06:14 +00:00
|
|
|
// Assumes we have a pipeline already
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mBindgroupsSet.all()) {
|
2017-07-07 23:06:14 +00:00
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
for (size_t i = 0; i < mBindgroups.size(); ++i) {
|
|
|
|
if (auto* bindgroup = mBindgroups[i]) {
|
2017-07-07 23:06:14 +00:00
|
|
|
// TODO(kainino@chromium.org): bind group compatibility
|
2018-06-27 23:21:39 +00:00
|
|
|
auto* pipelineBGL = mLastPipeline->GetLayout()->GetBindGroupLayout(i);
|
|
|
|
if (pipelineBGL && bindgroup->GetLayout() != pipelineBGL) {
|
2017-07-07 23:06:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
|
2017-07-07 23:06:14 +00:00
|
|
|
return true;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::RecomputeHaveAspectVertexBuffers() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mAspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-07-07 23:06:14 +00:00
|
|
|
// Assumes we have a pipeline already
|
2017-11-23 18:32:51 +00:00
|
|
|
auto requiredInputs = mLastRenderPipeline->GetInputState()->GetInputsSetMask();
|
|
|
|
if ((mInputsSet & requiredInputs) == requiredInputs) {
|
|
|
|
mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
2017-06-09 00:25:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CommandBufferStateTracker::HavePipeline() const {
|
|
|
|
constexpr ValidationAspects pipelineAspects =
|
2017-11-24 18:59:42 +00:00
|
|
|
1 << VALIDATION_ASPECT_COMPUTE_PIPELINE | 1 << VALIDATION_ASPECT_RENDER_PIPELINE;
|
2017-11-23 18:32:51 +00:00
|
|
|
return (mAspects & pipelineAspects).any();
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2017-11-24 18:59:42 +00:00
|
|
|
case nxt::BindingType::StorageBuffer: {
|
|
|
|
nxt::BufferUsageBit requiredUsage = nxt::BufferUsageBit::None;
|
|
|
|
switch (type) {
|
|
|
|
case nxt::BindingType::UniformBuffer:
|
|
|
|
requiredUsage = nxt::BufferUsageBit::Uniform;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nxt::BindingType::StorageBuffer:
|
|
|
|
requiredUsage = nxt::BufferUsageBit::Storage;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buffer = group->GetBindingAsBufferView(i)->GetBuffer();
|
|
|
|
if (!BufferHasGuaranteedUsageBit(buffer, requiredUsage)) {
|
|
|
|
mBuilder->HandleError("Can't guarantee buffer usage needed by bind group");
|
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-11-24 18:59:42 +00:00
|
|
|
} break;
|
|
|
|
case nxt::BindingType::SampledTexture: {
|
|
|
|
auto requiredUsage = nxt::TextureUsageBit::Sampled;
|
|
|
|
|
|
|
|
auto texture = group->GetBindingAsTextureView(i)->GetTexture();
|
|
|
|
if (!TextureHasGuaranteedUsageBit(texture, requiredUsage)) {
|
|
|
|
mBuilder->HandleError("Can't guarantee texture usage needed by bind group");
|
|
|
|
return false;
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-11-24 18:59:42 +00:00
|
|
|
} break;
|
2017-06-09 00:25:57 +00:00
|
|
|
case nxt::BindingType::Sampler:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2017-07-22 00:00:22 +00:00
|
|
|
}
|
2017-06-09 00:25:57 +00:00
|
|
|
|
|
|
|
bool CommandBufferStateTracker::RevalidateCanDraw() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mAspects[VALIDATION_ASPECT_RENDER_PIPELINE]) {
|
|
|
|
mBuilder->HandleError("No active render pipeline");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
// Compute the lazily computed mAspects
|
2017-06-09 00:25:57 +00:00
|
|
|
if (!RecomputeHaveAspectBindGroups()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Bind group state not valid");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!RecomputeHaveAspectVertexBuffers()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mBuilder->HandleError("Some vertex buffers are not set");
|
2017-06-09 00:25:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-31 17:56:15 +00:00
|
|
|
void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) {
|
2017-07-14 14:58:07 +00:00
|
|
|
PipelineLayoutBase* layout = pipeline->GetLayout();
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects.reset(VALIDATION_ASPECT_BIND_GROUPS);
|
|
|
|
mAspects.reset(VALIDATION_ASPECT_VERTEX_BUFFERS);
|
2017-07-15 05:37:06 +00:00
|
|
|
// Reset bindgroups but mark unused bindgroups as valid
|
2017-11-23 18:32:51 +00:00
|
|
|
mBindgroupsSet = ~layout->GetBindGroupsLayoutMask();
|
2017-07-14 14:58:07 +00:00
|
|
|
|
|
|
|
// Only bindgroups that were not the same layout in the last pipeline need to be set again.
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mLastPipeline) {
|
|
|
|
mBindgroupsSet |= layout->InheritedGroupsMask(mLastPipeline->GetLayout());
|
2017-07-14 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mLastPipeline = pipeline;
|
2017-07-14 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
2017-06-09 00:25:57 +00:00
|
|
|
void CommandBufferStateTracker::UnsetPipeline() {
|
2017-07-11 01:44:06 +00:00
|
|
|
constexpr ValidationAspects pipelineDependentAspects =
|
2017-11-24 18:59:42 +00:00
|
|
|
1 << VALIDATION_ASPECT_RENDER_PIPELINE | 1 << VALIDATION_ASPECT_COMPUTE_PIPELINE |
|
|
|
|
1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS |
|
2017-07-11 01:44:06 +00:00
|
|
|
1 << VALIDATION_ASPECT_INDEX_BUFFER;
|
2017-11-23 18:32:51 +00:00
|
|
|
mAspects &= ~pipelineDependentAspects;
|
|
|
|
mBindgroups.fill(nullptr);
|
2017-06-09 00:25:57 +00:00
|
|
|
}
|
2017-11-24 18:59:42 +00:00
|
|
|
} // namespace backend
|