D3D12/Vulkan: Insert storage buffer barriers between compute pass dipatches
This patch extends the BindGroupTracker in the D3D12 and Vulkan backends to track bound storage buffers. We insert barriers between dispatches to properly synchronize writes to storage buffers. Bug: dawn:236 Change-Id: Iab3f964c345b64755557ab206e05a2ff7b0a3a1f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12301 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
2c8b5c6370
commit
7b3cc35cb6
2
BUILD.gn
2
BUILD.gn
|
@ -800,6 +800,7 @@ test("dawn_unittests") {
|
|||
"src/tests/unittests/validation/BufferValidationTests.cpp",
|
||||
"src/tests/unittests/validation/CommandBufferValidationTests.cpp",
|
||||
"src/tests/unittests/validation/ComputeIndirectValidationTests.cpp",
|
||||
"src/tests/unittests/validation/ComputePassValidationTests.cpp",
|
||||
"src/tests/unittests/validation/ComputeValidationTests.cpp",
|
||||
"src/tests/unittests/validation/CopyCommandsValidationTests.cpp",
|
||||
"src/tests/unittests/validation/DebugMarkerValidationTests.cpp",
|
||||
|
@ -872,6 +873,7 @@ source_set("dawn_end2end_tests_sources") {
|
|||
"src/tests/end2end/ComputeCopyStorageBufferTests.cpp",
|
||||
"src/tests/end2end/ComputeIndirectTests.cpp",
|
||||
"src/tests/end2end/ComputeSharedMemoryTests.cpp",
|
||||
"src/tests/end2end/ComputeStorageBufferBarrierTests.cpp",
|
||||
"src/tests/end2end/CopyTests.cpp",
|
||||
"src/tests/end2end/CullingTests.cpp",
|
||||
"src/tests/end2end/DebugMarkerTests.cpp",
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2019 The Dawn 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 DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
|
||||
#define DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
|
||||
|
||||
#include "dawn_native/BindGroupTracker.h"
|
||||
|
||||
#include "dawn_native/BindGroup.h"
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
// Extends BindGroupTrackerBase to also keep track of resources that need a usage transition.
|
||||
template <bool CanInheritBindGroups, typename DynamicOffset = uint64_t>
|
||||
class BindGroupAndStorageBarrierTrackerBase
|
||||
: public BindGroupTrackerBase<CanInheritBindGroups, DynamicOffset> {
|
||||
using Base = BindGroupTrackerBase<CanInheritBindGroups, DynamicOffset>;
|
||||
|
||||
public:
|
||||
BindGroupAndStorageBarrierTrackerBase() = default;
|
||||
|
||||
void OnSetBindGroup(uint32_t index,
|
||||
BindGroupBase* bindGroup,
|
||||
uint32_t dynamicOffsetCount,
|
||||
uint64_t* dynamicOffsets) {
|
||||
if (this->mBindGroups[index] != bindGroup) {
|
||||
mBuffers[index] = {};
|
||||
mBuffersNeedingBarrier[index] = {};
|
||||
|
||||
const BindGroupLayoutBase* layout = bindGroup->GetLayout();
|
||||
const auto& info = layout->GetBindingInfo();
|
||||
|
||||
for (uint32_t binding : IterateBitSet(info.mask)) {
|
||||
if ((info.visibilities[binding] & dawn::ShaderStage::Compute) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mBindingTypes[index][binding] = info.types[binding];
|
||||
switch (info.types[binding]) {
|
||||
case dawn::BindingType::UniformBuffer:
|
||||
case dawn::BindingType::ReadonlyStorageBuffer:
|
||||
case dawn::BindingType::Sampler:
|
||||
case dawn::BindingType::SampledTexture:
|
||||
// Don't require barriers.
|
||||
break;
|
||||
|
||||
case dawn::BindingType::StorageBuffer:
|
||||
mBuffersNeedingBarrier[index].set(binding);
|
||||
mBuffers[index][binding] =
|
||||
bindGroup->GetBindingAsBufferBinding(binding).buffer;
|
||||
break;
|
||||
|
||||
case dawn::BindingType::StorageTexture:
|
||||
// Not implemented.
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base::OnSetBindGroup(index, bindGroup, dynamicOffsetCount, dynamicOffsets);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<std::bitset<kMaxBindingsPerGroup>, kMaxBindGroups> mBuffersNeedingBarrier = {};
|
||||
std::array<std::array<dawn::BindingType, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mBindingTypes = {};
|
||||
std::array<std::array<BufferBase*, kMaxBindingsPerGroup>, kMaxBindGroups> mBuffers = {};
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
||||
#endif // DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_
|
|
@ -27,15 +27,13 @@ namespace dawn_native {
|
|||
|
||||
// Keeps track of the dirty bind groups so they can be lazily applied when we know the
|
||||
// pipeline state or it changes.
|
||||
// |BindGroup| is a template parameter so a backend may provide its backend-specific
|
||||
// type or native handle.
|
||||
// |DynamicOffset| is a template parameter because offsets in Vulkan are uint32_t but uint64_t
|
||||
// in other backends.
|
||||
template <typename BindGroup, bool CanInheritBindGroups, typename DynamicOffset = uint64_t>
|
||||
template <bool CanInheritBindGroups, typename DynamicOffset = uint64_t>
|
||||
class BindGroupTrackerBase {
|
||||
public:
|
||||
void OnSetBindGroup(uint32_t index,
|
||||
BindGroup bindGroup,
|
||||
BindGroupBase* bindGroup,
|
||||
uint32_t dynamicOffsetCount,
|
||||
uint64_t* dynamicOffsets) {
|
||||
ASSERT(index < kMaxBindGroups);
|
||||
|
@ -103,7 +101,7 @@ namespace dawn_native {
|
|||
std::bitset<kMaxBindGroups> mDirtyBindGroups = 0;
|
||||
std::bitset<kMaxBindGroups> mDirtyBindGroupsObjectChangedOrIsDynamic = 0;
|
||||
std::bitset<kMaxBindGroups> mBindGroupLayoutsMask = 0;
|
||||
std::array<BindGroup, kMaxBindGroups> mBindGroups = {};
|
||||
std::array<BindGroupBase*, kMaxBindGroups> mBindGroups = {};
|
||||
std::array<uint32_t, kMaxBindGroups> mDynamicOffsetCounts = {};
|
||||
std::array<std::array<DynamicOffset, kMaxBindingsPerGroup>, kMaxBindGroups>
|
||||
mDynamicOffsets = {};
|
||||
|
|
|
@ -22,32 +22,16 @@ namespace dawn_native {
|
|||
void PassResourceUsageTracker::BufferUsedAs(BufferBase* buffer, dawn::BufferUsage usage) {
|
||||
// std::map's operator[] will create the key and return 0 if the key didn't exist
|
||||
// before.
|
||||
dawn::BufferUsage& storedUsage = mBufferUsages[buffer];
|
||||
|
||||
if (usage == dawn::BufferUsage::Storage && storedUsage & dawn::BufferUsage::Storage) {
|
||||
mStorageUsedMultipleTimes = true;
|
||||
}
|
||||
|
||||
storedUsage |= usage;
|
||||
mBufferUsages[buffer] |= usage;
|
||||
}
|
||||
|
||||
void PassResourceUsageTracker::TextureUsedAs(TextureBase* texture, dawn::TextureUsage usage) {
|
||||
// std::map's operator[] will create the key and return 0 if the key didn't exist
|
||||
// before.
|
||||
dawn::TextureUsage& storedUsage = mTextureUsages[texture];
|
||||
|
||||
if (usage == dawn::TextureUsage::Storage && storedUsage & dawn::TextureUsage::Storage) {
|
||||
mStorageUsedMultipleTimes = true;
|
||||
}
|
||||
|
||||
storedUsage |= usage;
|
||||
mTextureUsages[texture] |= usage;
|
||||
}
|
||||
|
||||
MaybeError PassResourceUsageTracker::ValidateComputePassUsages() const {
|
||||
// Storage resources cannot be used twice in the same compute pass
|
||||
if (mStorageUsedMultipleTimes) {
|
||||
return DAWN_VALIDATION_ERROR("Storage resource used multiple times in compute pass");
|
||||
}
|
||||
return ValidateUsages();
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ namespace dawn_native {
|
|||
|
||||
std::map<BufferBase*, dawn::BufferUsage> mBufferUsages;
|
||||
std::map<TextureBase*, dawn::TextureUsage> mTextureUsages;
|
||||
bool mStorageUsedMultipleTimes = false;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -140,14 +140,28 @@ namespace dawn_native { namespace d3d12 {
|
|||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage);
|
||||
D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage);
|
||||
|
||||
// If the transition is from-UAV-to-UAV, then a UAV barrier is needed.
|
||||
// If one of the usages isn't UAV, then other barriers are used.
|
||||
bool needsUAVBarrier = lastState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS &&
|
||||
newState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
|
||||
if (needsUAVBarrier) {
|
||||
barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
|
||||
barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier->UAV.pResource = GetD3D12Resource().Get();
|
||||
|
||||
mLastUsage = newUsage;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We can skip transitions to already current usages.
|
||||
// TODO(cwallez@chromium.org): Need some form of UAV barriers at some point.
|
||||
if ((mLastUsage & newUsage) == newUsage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage);
|
||||
D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage);
|
||||
mLastUsage = newUsage;
|
||||
|
||||
// The COMMON state represents a state where no write operations can be pending, which makes
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "dawn_native/d3d12/CommandBufferD3D12.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "dawn_native/BindGroupTracker.h"
|
||||
#include "dawn_native/BindGroupAndStorageBarrierTracker.h"
|
||||
#include "dawn_native/CommandEncoder.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/RenderBundle.h"
|
||||
|
@ -74,9 +74,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
class BindGroupStateTracker : public BindGroupTrackerBase<BindGroup*, false> {
|
||||
class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase<false> {
|
||||
public:
|
||||
BindGroupStateTracker(Device* device) : BindGroupTrackerBase(), mDevice(device) {
|
||||
BindGroupStateTracker(Device* device)
|
||||
: BindGroupAndStorageBarrierTrackerBase(), mDevice(device) {
|
||||
}
|
||||
|
||||
void SetInComputePass(bool inCompute_) {
|
||||
|
@ -137,10 +138,41 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
}
|
||||
|
||||
void Apply(ID3D12GraphicsCommandList* commandList) {
|
||||
void Apply(CommandRecordingContext* commandContext) {
|
||||
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
||||
|
||||
for (uint32_t index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) {
|
||||
ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index, mBindGroups[index],
|
||||
mDynamicOffsetCounts[index], mDynamicOffsets[index].data());
|
||||
ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index,
|
||||
ToBackend(mBindGroups[index]), mDynamicOffsetCounts[index],
|
||||
mDynamicOffsets[index].data());
|
||||
}
|
||||
|
||||
if (mInCompute) {
|
||||
for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) {
|
||||
for (uint32_t binding : IterateBitSet(mBuffersNeedingBarrier[index])) {
|
||||
dawn::BindingType bindingType = mBindingTypes[index][binding];
|
||||
switch (bindingType) {
|
||||
case dawn::BindingType::StorageBuffer:
|
||||
ToBackend(mBuffers[index][binding])
|
||||
->TransitionUsageNow(commandContext,
|
||||
dawn::BufferUsage::Storage);
|
||||
break;
|
||||
|
||||
case dawn::BindingType::StorageTexture:
|
||||
// Not implemented.
|
||||
|
||||
case dawn::BindingType::UniformBuffer:
|
||||
case dawn::BindingType::ReadonlyStorageBuffer:
|
||||
case dawn::BindingType::Sampler:
|
||||
case dawn::BindingType::SampledTexture:
|
||||
// Don't require barriers.
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DidApply();
|
||||
}
|
||||
|
@ -620,7 +652,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
|
||||
bindingTracker.SetInComputePass(true);
|
||||
RecordComputePass(commandList, &bindingTracker);
|
||||
RecordComputePass(commandContext, &bindingTracker);
|
||||
|
||||
nextPassNumber++;
|
||||
} break;
|
||||
|
@ -781,9 +813,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
return {};
|
||||
}
|
||||
|
||||
void CommandBuffer::RecordComputePass(ID3D12GraphicsCommandList* commandList,
|
||||
void CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext,
|
||||
BindGroupStateTracker* bindingTracker) {
|
||||
PipelineLayout* lastLayout = nullptr;
|
||||
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
||||
|
||||
Command type;
|
||||
while (mCommands.NextCommandId(&type)) {
|
||||
|
@ -791,14 +824,14 @@ namespace dawn_native { namespace d3d12 {
|
|||
case Command::Dispatch: {
|
||||
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z);
|
||||
} break;
|
||||
|
||||
case Command::DispatchIndirect: {
|
||||
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
|
||||
ComPtr<ID3D12CommandSignature> signature =
|
||||
ToBackend(GetDevice())->GetDispatchIndirectSignature();
|
||||
|
@ -1023,7 +1056,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
case Command::Draw: {
|
||||
DrawCmd* draw = iter->NextCommand<DrawCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
vertexBufferTracker.Apply(commandList, lastPipeline);
|
||||
commandList->DrawInstanced(draw->vertexCount, draw->instanceCount,
|
||||
draw->firstVertex, draw->firstInstance);
|
||||
|
@ -1032,7 +1065,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
case Command::DrawIndexed: {
|
||||
DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
indexBufferTracker.Apply(commandList);
|
||||
vertexBufferTracker.Apply(commandList, lastPipeline);
|
||||
commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount,
|
||||
|
@ -1043,7 +1076,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
case Command::DrawIndirect: {
|
||||
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
vertexBufferTracker.Apply(commandList, lastPipeline);
|
||||
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
|
||||
ComPtr<ID3D12CommandSignature> signature =
|
||||
|
@ -1056,7 +1089,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
case Command::DrawIndexedIndirect: {
|
||||
DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
|
||||
|
||||
bindingTracker->Apply(commandList);
|
||||
bindingTracker->Apply(commandContext);
|
||||
indexBufferTracker.Apply(commandList);
|
||||
vertexBufferTracker.Apply(commandList, lastPipeline);
|
||||
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
MaybeError RecordCommands(CommandRecordingContext* commandContext, uint32_t indexInSubmit);
|
||||
|
||||
private:
|
||||
void RecordComputePass(ID3D12GraphicsCommandList* commandList,
|
||||
void RecordComputePass(CommandRecordingContext* commandContext,
|
||||
BindGroupStateTracker* bindingTracker);
|
||||
void RecordRenderPass(CommandRecordingContext* commandContext,
|
||||
BindGroupStateTracker* bindingTracker,
|
||||
|
|
|
@ -394,7 +394,7 @@ namespace dawn_native { namespace metal {
|
|||
// pipeline state.
|
||||
// Bind groups may be inherited because bind groups are packed in the buffer /
|
||||
// texture tables in contiguous order.
|
||||
class BindGroupTracker : public BindGroupTrackerBase<BindGroup*, true> {
|
||||
class BindGroupTracker : public BindGroupTrackerBase<true> {
|
||||
public:
|
||||
explicit BindGroupTracker(StorageBufferLengthTracker* lengthTracker)
|
||||
: BindGroupTrackerBase(), mLengthTracker(lengthTracker) {
|
||||
|
@ -403,8 +403,9 @@ namespace dawn_native { namespace metal {
|
|||
template <typename Encoder>
|
||||
void Apply(Encoder encoder) {
|
||||
for (uint32_t index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) {
|
||||
ApplyBindGroup(encoder, index, mBindGroups[index], mDynamicOffsetCounts[index],
|
||||
mDynamicOffsets[index].data(), ToBackend(mPipelineLayout));
|
||||
ApplyBindGroup(encoder, index, ToBackend(mBindGroups[index]),
|
||||
mDynamicOffsetCounts[index], mDynamicOffsets[index].data(),
|
||||
ToBackend(mPipelineLayout));
|
||||
}
|
||||
DidApply();
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ namespace dawn_native { namespace opengl {
|
|||
RenderPipelineBase* mLastPipeline = nullptr;
|
||||
};
|
||||
|
||||
class BindGroupTracker : public BindGroupTrackerBase<BindGroupBase*, false> {
|
||||
class BindGroupTracker : public BindGroupTrackerBase<false> {
|
||||
public:
|
||||
void OnSetPipeline(RenderPipeline* pipeline) {
|
||||
BindGroupTrackerBase::OnSetPipeline(pipeline);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include "dawn_native/vulkan/CommandBufferVk.h"
|
||||
|
||||
#include "dawn_native/BindGroupTracker.h"
|
||||
#include "dawn_native/BindGroupAndStorageBarrierTracker.h"
|
||||
#include "dawn_native/CommandEncoder.h"
|
||||
#include "dawn_native/Commands.h"
|
||||
#include "dawn_native/RenderBundle.h"
|
||||
|
@ -91,16 +91,77 @@ namespace dawn_native { namespace vulkan {
|
|||
return region;
|
||||
}
|
||||
|
||||
class DescriptorSetTracker : public BindGroupTrackerBase<VkDescriptorSet, true, uint32_t> {
|
||||
void ApplyDescriptorSets(Device* device,
|
||||
VkCommandBuffer commands,
|
||||
VkPipelineBindPoint bindPoint,
|
||||
VkPipelineLayout pipelineLayout,
|
||||
const std::bitset<kMaxBindGroups>& bindGroupsToApply,
|
||||
const std::array<BindGroupBase*, kMaxBindGroups>& bindGroups,
|
||||
const std::array<uint32_t, kMaxBindGroups>& dynamicOffsetCounts,
|
||||
const std::array<std::array<uint32_t, kMaxBindingsPerGroup>,
|
||||
kMaxBindGroups>& dynamicOffsets) {
|
||||
for (uint32_t dirtyIndex : IterateBitSet(bindGroupsToApply)) {
|
||||
VkDescriptorSet set = ToBackend(bindGroups[dirtyIndex])->GetHandle();
|
||||
const uint32_t* dynamicOffset = dynamicOffsetCounts[dirtyIndex] > 0
|
||||
? dynamicOffsets[dirtyIndex].data()
|
||||
: nullptr;
|
||||
device->fn.CmdBindDescriptorSets(commands, bindPoint, pipelineLayout, dirtyIndex, 1,
|
||||
&set, dynamicOffsetCounts[dirtyIndex],
|
||||
dynamicOffset);
|
||||
}
|
||||
}
|
||||
|
||||
class RenderDescriptorSetTracker : public BindGroupTrackerBase<true, uint32_t> {
|
||||
public:
|
||||
void Apply(Device* device, VkCommandBuffer commands, VkPipelineBindPoint bindPoint) {
|
||||
for (uint32_t dirtyIndex :
|
||||
IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) {
|
||||
device->fn.CmdBindDescriptorSets(
|
||||
commands, bindPoint, ToBackend(mPipelineLayout)->GetHandle(), dirtyIndex, 1,
|
||||
&mBindGroups[dirtyIndex], mDynamicOffsetCounts[dirtyIndex],
|
||||
mDynamicOffsetCounts[dirtyIndex] > 0 ? mDynamicOffsets[dirtyIndex].data()
|
||||
: nullptr);
|
||||
RenderDescriptorSetTracker() = default;
|
||||
|
||||
void Apply(Device* device,
|
||||
CommandRecordingContext* recordingContext,
|
||||
VkPipelineBindPoint bindPoint) {
|
||||
ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint,
|
||||
ToBackend(mPipelineLayout)->GetHandle(),
|
||||
mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups,
|
||||
mDynamicOffsetCounts, mDynamicOffsets);
|
||||
DidApply();
|
||||
}
|
||||
};
|
||||
|
||||
class ComputeDescriptorSetTracker
|
||||
: public BindGroupAndStorageBarrierTrackerBase<true, uint32_t> {
|
||||
public:
|
||||
ComputeDescriptorSetTracker() = default;
|
||||
|
||||
void Apply(Device* device,
|
||||
CommandRecordingContext* recordingContext,
|
||||
VkPipelineBindPoint bindPoint) {
|
||||
ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint,
|
||||
ToBackend(mPipelineLayout)->GetHandle(),
|
||||
mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups,
|
||||
mDynamicOffsetCounts, mDynamicOffsets);
|
||||
|
||||
for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) {
|
||||
for (uint32_t binding : IterateBitSet(mBuffersNeedingBarrier[index])) {
|
||||
switch (mBindingTypes[index][binding]) {
|
||||
case dawn::BindingType::StorageBuffer:
|
||||
ToBackend(mBuffers[index][binding])
|
||||
->TransitionUsageNow(recordingContext,
|
||||
dawn::BufferUsage::Storage);
|
||||
break;
|
||||
|
||||
case dawn::BindingType::StorageTexture:
|
||||
// Not implemented.
|
||||
|
||||
case dawn::BindingType::UniformBuffer:
|
||||
case dawn::BindingType::ReadonlyStorageBuffer:
|
||||
case dawn::BindingType::Sampler:
|
||||
case dawn::BindingType::SampledTexture:
|
||||
// Don't require barriers.
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DidApply();
|
||||
}
|
||||
|
@ -553,7 +614,7 @@ namespace dawn_native { namespace vulkan {
|
|||
Device* device = ToBackend(GetDevice());
|
||||
VkCommandBuffer commands = recordingContext->commandBuffer;
|
||||
|
||||
DescriptorSetTracker descriptorSets = {};
|
||||
ComputeDescriptorSetTracker descriptorSets = {};
|
||||
|
||||
Command type;
|
||||
while (mCommands.NextCommandId(&type)) {
|
||||
|
@ -565,7 +626,8 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
case Command::Dispatch: {
|
||||
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z);
|
||||
} break;
|
||||
|
||||
|
@ -573,7 +635,7 @@ namespace dawn_native { namespace vulkan {
|
|||
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
|
||||
VkBuffer indirectBuffer = ToBackend(dispatch->indirectBuffer)->GetHandle();
|
||||
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
device->fn.CmdDispatchIndirect(
|
||||
commands, indirectBuffer,
|
||||
static_cast<VkDeviceSize>(dispatch->indirectOffset));
|
||||
|
@ -581,13 +643,14 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
case Command::SetBindGroup: {
|
||||
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
|
||||
VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();
|
||||
|
||||
BindGroup* bindGroup = ToBackend(cmd->group.Get());
|
||||
uint64_t* dynamicOffsets = nullptr;
|
||||
if (cmd->dynamicOffsetCount > 0) {
|
||||
dynamicOffsets = mCommands.NextData<uint64_t>(cmd->dynamicOffsetCount);
|
||||
}
|
||||
|
||||
descriptorSets.OnSetBindGroup(cmd->index, set, cmd->dynamicOffsetCount,
|
||||
descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount,
|
||||
dynamicOffsets);
|
||||
} break;
|
||||
|
||||
|
@ -695,7 +758,7 @@ namespace dawn_native { namespace vulkan {
|
|||
device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
|
||||
}
|
||||
|
||||
DescriptorSetTracker descriptorSets = {};
|
||||
RenderDescriptorSetTracker descriptorSets = {};
|
||||
RenderPipeline* lastPipeline = nullptr;
|
||||
|
||||
auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) {
|
||||
|
@ -703,7 +766,7 @@ namespace dawn_native { namespace vulkan {
|
|||
case Command::Draw: {
|
||||
DrawCmd* draw = iter->NextCommand<DrawCmd>();
|
||||
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
|
||||
draw->firstVertex, draw->firstInstance);
|
||||
} break;
|
||||
|
@ -711,7 +774,7 @@ namespace dawn_native { namespace vulkan {
|
|||
case Command::DrawIndexed: {
|
||||
DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
|
||||
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
|
||||
draw->firstIndex, draw->baseVertex,
|
||||
draw->firstInstance);
|
||||
|
@ -721,7 +784,7 @@ namespace dawn_native { namespace vulkan {
|
|||
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
|
||||
VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle();
|
||||
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
device->fn.CmdDrawIndirect(commands, indirectBuffer,
|
||||
static_cast<VkDeviceSize>(draw->indirectOffset), 1,
|
||||
0);
|
||||
|
@ -731,7 +794,7 @@ namespace dawn_native { namespace vulkan {
|
|||
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
|
||||
VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle();
|
||||
|
||||
descriptorSets.Apply(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
device->fn.CmdDrawIndexedIndirect(
|
||||
commands, indirectBuffer, static_cast<VkDeviceSize>(draw->indirectOffset),
|
||||
1, 0);
|
||||
|
@ -786,13 +849,13 @@ namespace dawn_native { namespace vulkan {
|
|||
|
||||
case Command::SetBindGroup: {
|
||||
SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
|
||||
VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();
|
||||
BindGroup* bindGroup = ToBackend(cmd->group.Get());
|
||||
uint64_t* dynamicOffsets = nullptr;
|
||||
if (cmd->dynamicOffsetCount > 0) {
|
||||
dynamicOffsets = iter->NextData<uint64_t>(cmd->dynamicOffsetCount);
|
||||
}
|
||||
|
||||
descriptorSets.OnSetBindGroup(cmd->index, set, cmd->dynamicOffsetCount,
|
||||
descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount,
|
||||
dynamicOffsets);
|
||||
} break;
|
||||
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2019 The Dawn 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 "tests/DawnTest.h"
|
||||
|
||||
#include "utils/DawnHelpers.h"
|
||||
|
||||
class ComputeStorageBufferBarrierTests : public DawnTest {
|
||||
protected:
|
||||
static constexpr uint32_t kNumValues = 100;
|
||||
static constexpr uint32_t kIterations = 100;
|
||||
};
|
||||
|
||||
// Test that multiple dispatches to increment values in a storage buffer are synchronized.
|
||||
TEST_P(ComputeStorageBufferBarrierTests, AddIncrement) {
|
||||
std::vector<uint32_t> data(kNumValues, 0);
|
||||
std::vector<uint32_t> expected(kNumValues, 0x1234 * kIterations);
|
||||
|
||||
uint64_t bufferSize = static_cast<uint64_t>(data.size() * sizeof(uint32_t));
|
||||
dawn::Buffer buffer = utils::CreateBufferFromData(
|
||||
device, data.data(), bufferSize, dawn::BufferUsage::Storage | dawn::BufferUsage::CopySrc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
#define kNumValues 100
|
||||
layout(std430, set = 0, binding = 0) buffer Buf { uint buf[kNumValues]; };
|
||||
void main() {
|
||||
buf[gl_GlobalInvocationID.x] += 0x1234;
|
||||
}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, bufferSize}});
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
for (uint32_t i = 0; i < kIterations; ++i) {
|
||||
pass.Dispatch(kNumValues, 1, 1);
|
||||
}
|
||||
pass.EndPass();
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), buffer, 0, kNumValues);
|
||||
}
|
||||
|
||||
// Test that multiple dispatches to increment values by ping-ponging between two storage buffers
|
||||
// are synchronized.
|
||||
TEST_P(ComputeStorageBufferBarrierTests, AddPingPong) {
|
||||
std::vector<uint32_t> data(kNumValues, 0);
|
||||
std::vector<uint32_t> expectedA(kNumValues, 0x1234 * kIterations);
|
||||
std::vector<uint32_t> expectedB(kNumValues, 0x1234 * (kIterations - 1));
|
||||
|
||||
uint64_t bufferSize = static_cast<uint64_t>(data.size() * sizeof(uint32_t));
|
||||
|
||||
dawn::Buffer bufferA = utils::CreateBufferFromData(
|
||||
device, data.data(), bufferSize, dawn::BufferUsage::Storage | dawn::BufferUsage::CopySrc);
|
||||
|
||||
dawn::Buffer bufferB = utils::CreateBufferFromData(
|
||||
device, data.data(), bufferSize, dawn::BufferUsage::Storage | dawn::BufferUsage::CopySrc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
#define kNumValues 100
|
||||
layout(std430, set = 0, binding = 0) buffer Src { uint src[kNumValues]; };
|
||||
layout(std430, set = 0, binding = 1) buffer Dst { uint dst[kNumValues]; };
|
||||
void main() {
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
dst[index] = src[index] + 0x1234;
|
||||
}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer},
|
||||
{1, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroupA = utils::MakeBindGroup(device, bgl,
|
||||
{
|
||||
{0, bufferA, 0, bufferSize},
|
||||
{1, bufferB, 0, bufferSize},
|
||||
});
|
||||
|
||||
dawn::BindGroup bindGroupB = utils::MakeBindGroup(device, bgl,
|
||||
{
|
||||
{0, bufferB, 0, bufferSize},
|
||||
{1, bufferA, 0, bufferSize},
|
||||
});
|
||||
|
||||
dawn::BindGroup bindGroups[2] = {bindGroupA, bindGroupB};
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
|
||||
for (uint32_t i = 0; i < kIterations / 2; ++i) {
|
||||
pass.SetBindGroup(0, bindGroups[0]);
|
||||
pass.Dispatch(kNumValues, 1, 1);
|
||||
pass.SetBindGroup(0, bindGroups[1]);
|
||||
pass.Dispatch(kNumValues, 1, 1);
|
||||
}
|
||||
pass.EndPass();
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedA.data(), bufferA, 0, kNumValues);
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedB.data(), bufferB, 0, kNumValues);
|
||||
}
|
||||
|
||||
// Test that Storage to Uniform buffer transitions work and synchronize correctly
|
||||
// by ping-ponging between Storage/Uniform usage in sequential compute passes.
|
||||
TEST_P(ComputeStorageBufferBarrierTests, UniformToStorageAddPingPong) {
|
||||
std::vector<uint32_t> data(kNumValues, 0);
|
||||
std::vector<uint32_t> expectedA(kNumValues, 0x1234 * kIterations);
|
||||
std::vector<uint32_t> expectedB(kNumValues, 0x1234 * (kIterations - 1));
|
||||
|
||||
uint64_t bufferSize = static_cast<uint64_t>(data.size() * sizeof(uint32_t));
|
||||
|
||||
dawn::Buffer bufferA = utils::CreateBufferFromData(
|
||||
device, data.data(), bufferSize, dawn::BufferUsage::Storage | dawn::BufferUsage::Uniform | dawn::BufferUsage::CopySrc);
|
||||
|
||||
dawn::Buffer bufferB = utils::CreateBufferFromData(
|
||||
device, data.data(), bufferSize, dawn::BufferUsage::Storage | dawn::BufferUsage::Uniform | dawn::BufferUsage::CopySrc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
#define kNumValues 100
|
||||
layout(std140, set = 0, binding = 0) uniform Src { uvec4 src[kNumValues / 4]; };
|
||||
layout(std430, set = 0, binding = 1) buffer Dst { uvec4 dst[kNumValues / 4]; };
|
||||
void main() {
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
dst[index] = src[index] + 0x1234;
|
||||
}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::UniformBuffer},
|
||||
{1, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroupA = utils::MakeBindGroup(device, bgl,
|
||||
{
|
||||
{0, bufferA, 0, bufferSize},
|
||||
{1, bufferB, 0, bufferSize},
|
||||
});
|
||||
|
||||
dawn::BindGroup bindGroupB = utils::MakeBindGroup(device, bgl,
|
||||
{
|
||||
{0, bufferB, 0, bufferSize},
|
||||
{1, bufferA, 0, bufferSize},
|
||||
});
|
||||
|
||||
dawn::BindGroup bindGroups[2] = {bindGroupA, bindGroupB};
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
|
||||
for (uint32_t i = 0, b = 0; i < kIterations; ++i, b = 1 - b) {
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
pass.SetBindGroup(0, bindGroups[b]);
|
||||
pass.Dispatch(kNumValues, 1, 1);
|
||||
pass.EndPass();
|
||||
}
|
||||
|
||||
dawn::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedA.data(), bufferA, 0, kNumValues);
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedB.data(), bufferB, 0, kNumValues);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(ComputeStorageBufferBarrierTests,
|
||||
D3D12Backend,
|
||||
MetalBackend,
|
||||
OpenGLBackend,
|
||||
VulkanBackend);
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2019 The Dawn 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 "tests/unittests/validation/ValidationTest.h"
|
||||
|
||||
#include "utils/DawnHelpers.h"
|
||||
|
||||
class ComputePassValidationTest : public ValidationTest {};
|
||||
|
||||
// Test that it is invalid to use a buffer with both read and write usages in a compute pass.
|
||||
TEST_F(ComputePassValidationTest, ReadWriteUsage) {
|
||||
dawn::BufferDescriptor bufferDesc = {};
|
||||
bufferDesc.usage = dawn::BufferUsage::Storage | dawn::BufferUsage::Uniform;
|
||||
bufferDesc.size = 4;
|
||||
dawn::Buffer buffer = device.CreateBuffer(&bufferDesc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
layout(std430, set = 0, binding = 0) buffer BufA { uint bufA; };
|
||||
layout(std140, set = 0, binding = 1) uniform BufB { uint bufB; };
|
||||
void main() {}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer},
|
||||
{1, dawn::ShaderStage::Compute, dawn::BindingType::UniformBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl,
|
||||
{
|
||||
{0, buffer, 0, 4},
|
||||
{1, buffer, 0, 4},
|
||||
});
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
|
||||
pass.Dispatch(1, 1, 1);
|
||||
pass.Dispatch(1, 1, 1);
|
||||
|
||||
pass.EndPass();
|
||||
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||
}
|
||||
|
||||
// Test that it is valid to use a buffer with a single write usage multiple times in a compute pass.
|
||||
TEST_F(ComputePassValidationTest, MultipleWrites) {
|
||||
dawn::BufferDescriptor bufferDesc = {};
|
||||
bufferDesc.usage = dawn::BufferUsage::Storage;
|
||||
bufferDesc.size = 4;
|
||||
dawn::Buffer buffer = device.CreateBuffer(&bufferDesc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
layout(std430, set = 0, binding = 0) buffer Buf { uint buf; };
|
||||
void main() {}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}});
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
|
||||
pass.Dispatch(1, 1, 1);
|
||||
pass.Dispatch(1, 1, 1);
|
||||
|
||||
pass.EndPass();
|
||||
encoder.Finish();
|
||||
}
|
||||
|
||||
// Test that it is valid to use a buffer with a single write usage multiple times in a compute pass,
|
||||
// even if the buffer is referenced in separate bind groups.
|
||||
TEST_F(ComputePassValidationTest, MultipleWritesSeparateBindGroups) {
|
||||
dawn::BufferDescriptor bufferDesc = {};
|
||||
bufferDesc.usage = dawn::BufferUsage::Storage;
|
||||
bufferDesc.size = 4;
|
||||
dawn::Buffer buffer = device.CreateBuffer(&bufferDesc);
|
||||
|
||||
dawn::ShaderModule module =
|
||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
|
||||
#version 450
|
||||
#define kNumValues 100
|
||||
layout(std430, set = 0, binding = 0) buffer Buf { uint buf; };
|
||||
void main() {}
|
||||
)");
|
||||
|
||||
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
|
||||
device, {{0, dawn::ShaderStage::Compute, dawn::BindingType::StorageBuffer}});
|
||||
|
||||
dawn::BindGroup bindGroupA = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}});
|
||||
dawn::BindGroup bindGroupB = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}});
|
||||
|
||||
dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, &bgl);
|
||||
|
||||
dawn::ComputePipelineDescriptor pipelineDesc = {};
|
||||
pipelineDesc.layout = layout;
|
||||
pipelineDesc.computeStage.module = module;
|
||||
pipelineDesc.computeStage.entryPoint = "main";
|
||||
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc);
|
||||
|
||||
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
|
||||
pass.SetPipeline(pipeline);
|
||||
|
||||
pass.SetBindGroup(0, bindGroupA);
|
||||
pass.Dispatch(1, 1, 1);
|
||||
|
||||
pass.SetBindGroup(0, bindGroupB);
|
||||
pass.Dispatch(1, 1, 1);
|
||||
|
||||
pass.EndPass();
|
||||
encoder.Finish();
|
||||
}
|
Loading…
Reference in New Issue